[PATCH 4/5] ddraw/tests: Add a test for getdc palette handling.

Stefan Dösinger stefan at codeweavers.com
Mon Apr 28 08:21:57 CDT 2014


---
 dlls/ddraw/tests/ddraw1.c | 300 +++++++++++++++++++++++++++++++++++++++++++++
 dlls/ddraw/tests/ddraw2.c | 301 ++++++++++++++++++++++++++++++++++++++++++++++
 dlls/ddraw/tests/ddraw4.c | 299 +++++++++++++++++++++++++++++++++++++++++++++
 dlls/ddraw/tests/ddraw7.c | 299 +++++++++++++++++++++++++++++++++++++++++++++
 dlls/ddraw/tests/visual.c |  23 ----
 5 files changed, 1199 insertions(+), 23 deletions(-)

diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c
index 65f56c2..a3c9b82 100644
--- a/dlls/ddraw/tests/ddraw1.c
+++ b/dlls/ddraw/tests/ddraw1.c
@@ -4807,6 +4807,305 @@ static void test_p8_rgb_blit(void)
     DestroyWindow(window);
 }
 
+static void test_palette_gdi(void)
+{
+    IDirectDrawSurface *surface, *primary;
+    DDSURFACEDESC surface_desc;
+    IDirectDraw *ddraw;
+    IDirectDrawPalette *palette, *palette2;
+    ULONG refcount;
+    HWND window;
+    HRESULT hr;
+    PALETTEENTRY palette_entries[256];
+    UINT i;
+    HDC dc;
+    /* On the Windows 8 testbot palette index 0 of the onscreen palette is forced to
+     * r = 0, g = 0, b = 0. Do not attempt to set it to something else as this is
+     * not the point of this test. */
+    static const RGBQUAD expected1[] =
+    {
+        {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x01, 0x00}, {0x00, 0x02, 0x00, 0x00},
+        {0x03, 0x00, 0x00, 0x00}, {0x15, 0x14, 0x13, 0x00},
+    };
+    static const RGBQUAD expected2[] =
+    {
+        {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x01, 0x00}, {0x00, 0x02, 0x00, 0x00},
+        {0x03, 0x00, 0x00, 0x00}, {0x25, 0x24, 0x23, 0x00},
+    };
+    static const RGBQUAD expected3[] =
+    {
+        {0x00, 0x00, 0x00, 0x00}, {0x40, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x40, 0x00},
+        {0x00, 0x40, 0x00, 0x00}, {0x56, 0x34, 0x12, 0x00},
+    };
+    HPALETTE ddraw_palette_handle;
+    /* Similar to index 0, index 255 is r = 0xff, g = 0xff, b = 0xff on the Win8 VMs. */
+    RGBQUAD rgbquad[255];
+    static const RGBQUAD rgb_zero = {0, 0, 0, 0};
+
+    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");
+    hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+    ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
+
+    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.dwWidth = 16;
+    surface_desc.dwHeight = 16;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    surface_desc.ddpfPixelFormat.dwSize = sizeof(surface_desc.ddpfPixelFormat);
+    surface_desc.ddpfPixelFormat.dwFlags = DDPF_PALETTEINDEXED8 | DDPF_RGB;
+    U1(surface_desc.ddpfPixelFormat).dwRGBBitCount = 8;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+    /* Avoid colors from the Windows default palette. */
+    memset(palette_entries, 0, sizeof(palette_entries));
+    palette_entries[1].peRed = 0x01;
+    palette_entries[2].peGreen = 0x02;
+    palette_entries[3].peBlue = 0x03;
+    palette_entries[4].peRed = 0x13;
+    palette_entries[4].peGreen = 0x14;
+    palette_entries[4].peBlue = 0x15;
+    hr = IDirectDraw_CreatePalette(ddraw, DDPCAPS_8BIT | DDPCAPS_ALLOW256,
+            palette_entries, &palette, NULL);
+    ok(SUCCEEDED(hr), "Failed to create palette, hr %#x.\n", hr);
+
+    /* If there is no palette assigned and the display mode is not 8 bpp, some
+     * drivers refuse to create a DC while others allow it. If a DC is created,
+     * the DIB color table is uninitialized and contains random colors. No error
+     * is generated when trying to read pixels and random garbage is returned.
+     *
+     * The most likely explanation is that if the driver creates a DC, it (or
+     * the higher-level runtime) uses GetSystemPaletteEntries to find the
+     * palette, but GetSystemPaletteEntries fails when bpp > 8 and the palette
+     * contains uninitialized garbage. See comments beflow for the P8 case. */
+
+    hr = IDirectDrawSurface_SetPalette(surface, palette);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    ddraw_palette_handle = SelectPalette(dc, GetStockObject(DEFAULT_PALETTE), FALSE);
+    todo_wine ok(ddraw_palette_handle == GetStockObject(DEFAULT_PALETTE),
+            "Got unexpected palette %p, expected %p.\n",
+            ddraw_palette_handle, GetStockObject(DEFAULT_PALETTE));
+    SelectPalette(dc, ddraw_palette_handle, FALSE);
+
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected1) / sizeof(*expected1); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected1[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected1[i].rgbRed, expected1[i].rgbGreen, expected1[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+
+    /* Update the palette while the DC is in use. This does not modify the DC. */
+    palette_entries[4].peRed = 0x23;
+    palette_entries[4].peGreen = 0x24;
+    palette_entries[4].peBlue = 0x25;
+    hr = IDirectDrawPalette_SetEntries(palette, 0, 4, 1, &palette_entries[4]);
+    ok(SUCCEEDED(hr), "Failed to set palette entries, hr %#x.\n", hr);
+
+    i = GetDIBColorTable(dc, 4, 1, &rgbquad[4]);
+    ok(i == 1, "Expected count 1, got %u.\n", i);
+    todo_wine ok(!memcmp(&rgbquad[4], &expected1[4], sizeof(rgbquad[4])),
+            "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+            i, rgbquad[4].rgbRed, rgbquad[4].rgbGreen, rgbquad[4].rgbBlue,
+            expected1[4].rgbRed, expected1[4].rgbGreen, expected1[4].rgbBlue);
+
+    /* Neither does re-setting the palette. */
+    hr = IDirectDrawSurface_SetPalette(surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface_SetPalette(surface, palette);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+
+    i = GetDIBColorTable(dc, 4, 1, &rgbquad[4]);
+    ok(i == 1, "Expected count 1, got %u.\n", i);
+    todo_wine ok(!memcmp(&rgbquad[4], &expected1[4], sizeof(rgbquad[4])),
+            "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+            i, rgbquad[4].rgbRed, rgbquad[4].rgbGreen, rgbquad[4].rgbBlue,
+            expected1[4].rgbRed, expected1[4].rgbGreen, expected1[4].rgbBlue);
+
+    hr = IDirectDrawSurface_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    /* Refresh the DC. This updates the palette. */
+    hr = IDirectDrawSurface_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected2) / sizeof(*expected2); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected2[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected2[i].rgbRed, expected2[i].rgbGreen, expected2[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    refcount = IDirectDrawSurface_Release(surface);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+
+    if (FAILED(hr = IDirectDraw_SetDisplayMode(ddraw, 640, 480, 8)))
+    {
+        win_skip("Failed to set 8 bpp display mode, skipping test.\n");
+        IDirectDrawPalette_Release(palette);
+        IDirectDraw_Release(ddraw);
+        DestroyWindow(window);
+        return;
+    }
+    ok(SUCCEEDED(hr), "Failed to set display mode, hr %#x.\n", hr);
+    hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE);
+    ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &primary, NULL);
+    ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+    hr = IDirectDrawSurface_SetPalette(primary, palette);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface_GetDC(primary, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    ddraw_palette_handle = SelectPalette(dc, GetStockObject(DEFAULT_PALETTE), FALSE);
+    /* Windows 2000 on the testbot assigns a different palette to the primary. Refrast? */
+    todo_wine ok(ddraw_palette_handle == GetStockObject(DEFAULT_PALETTE) || broken(TRUE),
+            "Got unexpected palette %p, expected %p.\n",
+            ddraw_palette_handle, GetStockObject(DEFAULT_PALETTE));
+    SelectPalette(dc, ddraw_palette_handle, FALSE);
+
+    /* The primary uses the system palette. In exclusive mode, the system palette matches
+     * the ddraw palette attached to the primary, so the result is what you would expect
+     * from a regular surface. Tests for the interaction between the ddraw palette and
+     * the system palette are not included pending an application that depends on this.
+     * The relation between those causes problems on Windows Vista and newer for games
+     * like Age of Empires or Starcraft. Don't emulate it without a real need. */
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected2) / sizeof(*expected2); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected2[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected2[i].rgbRed, expected2[i].rgbGreen, expected2[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface_ReleaseDC(primary, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.dwWidth = 16;
+    surface_desc.dwHeight = 16;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+    /* Here the offscreen surface appears to use the primary's palette,
+     * but in all likelyhood it is actually the system palette. */
+    hr = IDirectDrawSurface_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected2) / sizeof(*expected2); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected2[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected2[i].rgbRed, expected2[i].rgbGreen, expected2[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    /* On real hardware a change to the primary surface's palette applies immediately,
+     * even on device contexts from offscreen surfaces that do not have their own
+     * palette. On the testbot VMs this is not the case. Don't test this until we
+     * know of an application that depends on this. */
+
+    memset(palette_entries, 0, sizeof(palette_entries));
+    palette_entries[1].peBlue = 0x40;
+    palette_entries[2].peRed = 0x40;
+    palette_entries[3].peGreen = 0x40;
+    palette_entries[4].peRed = 0x12;
+    palette_entries[4].peGreen = 0x34;
+    palette_entries[4].peBlue = 0x56;
+    hr = IDirectDraw_CreatePalette(ddraw, DDPCAPS_8BIT | DDPCAPS_ALLOW256,
+            palette_entries, &palette2, NULL);
+    ok(SUCCEEDED(hr), "Failed to create palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface_SetPalette(surface, palette2);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+
+    /* A palette assigned to the offscreen surface overrides the primary / system
+     * palette. */
+    hr = IDirectDrawSurface_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected3) / sizeof(*expected3); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected3[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected3[i].rgbRed, expected3[i].rgbGreen, expected3[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    /* Windows 8 does not properly release the palette when the
+     * surface is destroyed. Sigh. */
+    hr = IDirectDrawSurface_SetPalette(primary, NULL);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+
+    refcount = IDirectDrawSurface_Release(surface);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    /* See Win8 refcounting remarks in test_primary_palette. */
+    while (IDirectDrawSurface_Release(primary));
+    refcount = IDirectDrawPalette_Release(palette2);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    refcount = IDirectDrawPalette_Release(palette);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    refcount = IDirectDraw_Release(ddraw);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    DestroyWindow(window);
+}
+
 START_TEST(ddraw1)
 {
     IDirectDraw *ddraw;
@@ -4854,4 +5153,5 @@ START_TEST(ddraw1)
     test_mipmap_lock();
     test_palette_complex();
     test_p8_rgb_blit();
+    test_palette_gdi();
 }
diff --git a/dlls/ddraw/tests/ddraw2.c b/dlls/ddraw/tests/ddraw2.c
index 4129de0..b92c2d3 100644
--- a/dlls/ddraw/tests/ddraw2.c
+++ b/dlls/ddraw/tests/ddraw2.c
@@ -5921,6 +5921,306 @@ static void test_p8_rgb_blit(void)
     DestroyWindow(window);
 }
 
+static void test_palette_gdi(void)
+{
+    IDirectDrawSurface *surface, *primary;
+    DDSURFACEDESC surface_desc;
+    IDirectDraw2 *ddraw;
+    IDirectDrawPalette *palette, *palette2;
+    ULONG refcount;
+    HWND window;
+    HRESULT hr;
+    PALETTEENTRY palette_entries[256];
+    UINT i;
+    HDC dc;
+    /* On the Windows 8 testbot palette index 0 of the onscreen palette is forced to
+     * r = 0, g = 0, b = 0. Do not attempt to set it to something else as this is
+     * not the point of this test. */
+    static const RGBQUAD expected1[] =
+    {
+        {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x01, 0x00}, {0x00, 0x02, 0x00, 0x00},
+        {0x03, 0x00, 0x00, 0x00}, {0x15, 0x14, 0x13, 0x00},
+    };
+    static const RGBQUAD expected2[] =
+    {
+        {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x01, 0x00}, {0x00, 0x02, 0x00, 0x00},
+        {0x03, 0x00, 0x00, 0x00}, {0x25, 0x24, 0x23, 0x00},
+    };
+    static const RGBQUAD expected3[] =
+    {
+        {0x00, 0x00, 0x00, 0x00}, {0x40, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x40, 0x00},
+        {0x00, 0x40, 0x00, 0x00}, {0x56, 0x34, 0x12, 0x00},
+    };
+    HPALETTE ddraw_palette_handle;
+    /* Similar to index 0, index 255 is r = 0xff, g = 0xff, b = 0xff on the Win8 VMs. */
+    RGBQUAD rgbquad[255];
+    static const RGBQUAD rgb_zero = {0, 0, 0, 0};
+
+    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");
+    hr = IDirectDraw2_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+    ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
+
+    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.dwWidth = 16;
+    surface_desc.dwHeight = 16;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    surface_desc.ddpfPixelFormat.dwSize = sizeof(surface_desc.ddpfPixelFormat);
+    surface_desc.ddpfPixelFormat.dwFlags = DDPF_PALETTEINDEXED8 | DDPF_RGB;
+    U1(surface_desc.ddpfPixelFormat).dwRGBBitCount = 8;
+    hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+    /* Avoid colors from the Windows default palette. */
+    memset(palette_entries, 0, sizeof(palette_entries));
+    palette_entries[1].peRed = 0x01;
+    palette_entries[2].peGreen = 0x02;
+    palette_entries[3].peBlue = 0x03;
+    palette_entries[4].peRed = 0x13;
+    palette_entries[4].peGreen = 0x14;
+    palette_entries[4].peBlue = 0x15;
+    hr = IDirectDraw2_CreatePalette(ddraw, DDPCAPS_8BIT | DDPCAPS_ALLOW256,
+            palette_entries, &palette, NULL);
+    ok(SUCCEEDED(hr), "Failed to create palette, hr %#x.\n", hr);
+
+    /* If there is no palette assigned and the display mode is not 8 bpp, some
+     * drivers refuse to create a DC while others allow it. If a DC is created,
+     * the DIB color table is uninitialized and contains random colors. No error
+     * is generated when trying to read pixels and random garbage is returned.
+     *
+     * The most likely explanation is that if the driver creates a DC, it (or
+     * the higher-level runtime) uses GetSystemPaletteEntries to find the
+     * palette, but GetSystemPaletteEntries fails when bpp > 8 and the palette
+     * contains uninitialized garbage. See comments beflow for the P8 case. */
+
+    hr = IDirectDrawSurface_SetPalette(surface, palette);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    ddraw_palette_handle = SelectPalette(dc, GetStockObject(DEFAULT_PALETTE), FALSE);
+    todo_wine ok(ddraw_palette_handle == GetStockObject(DEFAULT_PALETTE),
+            "Got unexpected palette %p, expected %p.\n",
+            ddraw_palette_handle, GetStockObject(DEFAULT_PALETTE));
+    SelectPalette(dc, ddraw_palette_handle, FALSE);
+
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected1) / sizeof(*expected1); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected1[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected1[i].rgbRed, expected1[i].rgbGreen, expected1[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+
+    /* Update the palette while the DC is in use. This does not modify the DC. */
+    palette_entries[4].peRed = 0x23;
+    palette_entries[4].peGreen = 0x24;
+    palette_entries[4].peBlue = 0x25;
+    hr = IDirectDrawPalette_SetEntries(palette, 0, 4, 1, &palette_entries[4]);
+    ok(SUCCEEDED(hr), "Failed to set palette entries, hr %#x.\n", hr);
+
+    i = GetDIBColorTable(dc, 4, 1, &rgbquad[4]);
+    ok(i == 1, "Expected count 1, got %u.\n", i);
+    todo_wine ok(!memcmp(&rgbquad[4], &expected1[4], sizeof(rgbquad[4])),
+            "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+            i, rgbquad[4].rgbRed, rgbquad[4].rgbGreen, rgbquad[4].rgbBlue,
+            expected1[4].rgbRed, expected1[4].rgbGreen, expected1[4].rgbBlue);
+
+    /* Neither does re-setting the palette. */
+    hr = IDirectDrawSurface_SetPalette(surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface_SetPalette(surface, palette);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+
+    i = GetDIBColorTable(dc, 4, 1, &rgbquad[4]);
+    ok(i == 1, "Expected count 1, got %u.\n", i);
+    todo_wine ok(!memcmp(&rgbquad[4], &expected1[4], sizeof(rgbquad[4])),
+            "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+            i, rgbquad[4].rgbRed, rgbquad[4].rgbGreen, rgbquad[4].rgbBlue,
+            expected1[4].rgbRed, expected1[4].rgbGreen, expected1[4].rgbBlue);
+
+    hr = IDirectDrawSurface_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    /* Refresh the DC. This updates the palette. */
+    hr = IDirectDrawSurface_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected2) / sizeof(*expected2); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected2[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected2[i].rgbRed, expected2[i].rgbGreen, expected2[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    refcount = IDirectDrawSurface_Release(surface);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+
+    if (FAILED(hr = IDirectDraw2_SetDisplayMode(ddraw, 640, 480, 8, 0, 0)))
+    {
+        win_skip("Failed to set 8 bpp display mode, skipping test.\n");
+        IDirectDrawPalette_Release(palette);
+        IDirectDraw2_Release(ddraw);
+        DestroyWindow(window);
+        return;
+    }
+    ok(SUCCEEDED(hr), "Failed to set display mode, hr %#x.\n", hr);
+    hr = IDirectDraw2_SetCooperativeLevel(ddraw, window, DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE);
+    ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+    hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &primary, NULL);
+    ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+    hr = IDirectDrawSurface_SetPalette(primary, palette);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface_GetDC(primary, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    ddraw_palette_handle = SelectPalette(dc, GetStockObject(DEFAULT_PALETTE), FALSE);
+    /* Windows 2000 on the testbot assigns a different palette to the primary. Refrast? */
+    todo_wine ok(ddraw_palette_handle == GetStockObject(DEFAULT_PALETTE) || broken(TRUE),
+            "Got unexpected palette %p, expected %p.\n",
+            ddraw_palette_handle, GetStockObject(DEFAULT_PALETTE));
+    SelectPalette(dc, ddraw_palette_handle, FALSE);
+
+    /* The primary uses the system palette. In exclusive mode, the system palette matches
+     * the ddraw palette attached to the primary, so the result is what you would expect
+     * from a regular surface. Tests for the interaction between the ddraw palette and
+     * the system palette are not included pending an application that depends on this.
+     * The relation between those causes problems on Windows Vista and newer for games
+     * like Age of Empires or Starcraft. Don't emulate it without a real need. */
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected2) / sizeof(*expected2); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected2[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected2[i].rgbRed, expected2[i].rgbGreen, expected2[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface_ReleaseDC(primary, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.dwWidth = 16;
+    surface_desc.dwHeight = 16;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+    /* Here the offscreen surface appears to use the primary's palette,
+     * but in all likelyhood it is actually the system palette. */
+    hr = IDirectDrawSurface_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected2) / sizeof(*expected2); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected2[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected2[i].rgbRed, expected2[i].rgbGreen, expected2[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    /* On real hardware a change to the primary surface's palette applies immediately,
+     * even on device contexts from offscreen surfaces that do not have their own
+     * palette. On the testbot VMs this is not the case. Don't test this until we
+     * know of an application that depends on this. */
+
+    memset(palette_entries, 0, sizeof(palette_entries));
+    palette_entries[1].peBlue = 0x40;
+    palette_entries[2].peRed = 0x40;
+    palette_entries[3].peGreen = 0x40;
+    palette_entries[4].peRed = 0x12;
+    palette_entries[4].peGreen = 0x34;
+    palette_entries[4].peBlue = 0x56;
+    hr = IDirectDraw2_CreatePalette(ddraw, DDPCAPS_8BIT | DDPCAPS_ALLOW256,
+            palette_entries, &palette2, NULL);
+    ok(SUCCEEDED(hr), "Failed to create palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface_SetPalette(surface, palette2);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+
+    /* A palette assigned to the offscreen surface overrides the primary / system
+     * palette. */
+    hr = IDirectDrawSurface_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected3) / sizeof(*expected3); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected3[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected3[i].rgbRed, expected3[i].rgbGreen, expected3[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    /* Windows 8 does not properly release the palette when the
+     * surface is destroyed. Sigh. */
+    hr = IDirectDrawSurface_SetPalette(primary, NULL);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+
+    refcount = IDirectDrawSurface_Release(surface);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    /* See Win8 refcounting remarks in test_primary_palette. */
+    while (IDirectDrawSurface_Release(primary));
+    refcount = IDirectDrawPalette_Release(palette2);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    refcount = IDirectDrawPalette_Release(palette);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    refcount = IDirectDraw2_Release(ddraw);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    DestroyWindow(window);
+}
+
+
 START_TEST(ddraw2)
 {
     IDirectDraw2 *ddraw;
@@ -5974,4 +6274,5 @@ START_TEST(ddraw2)
     test_mipmap_lock();
     test_palette_complex();
     test_p8_rgb_blit();
+    test_palette_gdi();
 }
diff --git a/dlls/ddraw/tests/ddraw4.c b/dlls/ddraw/tests/ddraw4.c
index 205fb57..5caf7ab 100644
--- a/dlls/ddraw/tests/ddraw4.c
+++ b/dlls/ddraw/tests/ddraw4.c
@@ -6782,6 +6782,304 @@ static void test_p8_rgb_blit(void)
     DestroyWindow(window);
 }
 
+static void test_palette_gdi(void)
+{
+    IDirectDrawSurface4 *surface, *primary;
+    DDSURFACEDESC2 surface_desc;
+    IDirectDraw4 *ddraw;
+    IDirectDrawPalette *palette, *palette2;
+    ULONG refcount;
+    HWND window;
+    HRESULT hr;
+    PALETTEENTRY palette_entries[256];
+    UINT i;
+    HDC dc;
+    /* On the Windows 8 testbot palette index 0 of the onscreen palette is forced to
+     * r = 0, g = 0, b = 0. Do not attempt to set it to something else as this is
+     * not the point of this test. */
+    static const RGBQUAD expected1[] =
+    {
+        {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x01, 0x00}, {0x00, 0x02, 0x00, 0x00},
+        {0x03, 0x00, 0x00, 0x00}, {0x15, 0x14, 0x13, 0x00},
+    };
+    static const RGBQUAD expected2[] =
+    {
+        {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x01, 0x00}, {0x00, 0x02, 0x00, 0x00},
+        {0x03, 0x00, 0x00, 0x00}, {0x25, 0x24, 0x23, 0x00},
+    };
+    static const RGBQUAD expected3[] =
+    {
+        {0x00, 0x00, 0x00, 0x00}, {0x40, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x40, 0x00},
+        {0x00, 0x40, 0x00, 0x00}, {0x56, 0x34, 0x12, 0x00},
+    };
+    HPALETTE ddraw_palette_handle;
+    /* Similar to index 0, index 255 is r = 0xff, g = 0xff, b = 0xff on the Win8 VMs. */
+    RGBQUAD rgbquad[255];
+    static const RGBQUAD rgb_zero = {0, 0, 0, 0};
+
+    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");
+    hr = IDirectDraw4_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+    ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
+
+    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.dwWidth = 16;
+    surface_desc.dwHeight = 16;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    U4(surface_desc).ddpfPixelFormat.dwSize = sizeof(U4(surface_desc).ddpfPixelFormat);
+    U4(surface_desc).ddpfPixelFormat.dwFlags = DDPF_PALETTEINDEXED8 | DDPF_RGB;
+    U1(U4(surface_desc).ddpfPixelFormat).dwRGBBitCount = 8;
+    hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+    /* Avoid colors from the Windows default palette. */
+    memset(palette_entries, 0, sizeof(palette_entries));
+    palette_entries[1].peRed = 0x01;
+    palette_entries[2].peGreen = 0x02;
+    palette_entries[3].peBlue = 0x03;
+    palette_entries[4].peRed = 0x13;
+    palette_entries[4].peGreen = 0x14;
+    palette_entries[4].peBlue = 0x15;
+    hr = IDirectDraw4_CreatePalette(ddraw, DDPCAPS_8BIT | DDPCAPS_ALLOW256,
+            palette_entries, &palette, NULL);
+    ok(SUCCEEDED(hr), "Failed to create palette, hr %#x.\n", hr);
+
+    /* If there is no palette assigned and the display mode is not 8 bpp, some
+     * drivers refuse to create a DC while others allow it. If a DC is created,
+     * the DIB color table is uninitialized and contains random colors. No error
+     * is generated when trying to read pixels and random garbage is returned.
+     *
+     * The most likely explanation is that if the driver creates a DC, it (or
+     * the higher-level runtime) uses GetSystemPaletteEntries to find the
+     * palette, but GetSystemPaletteEntries fails when bpp > 8 and the palette
+     * contains uninitialized garbage. See comments beflow for the P8 case. */
+
+    hr = IDirectDrawSurface4_SetPalette(surface, palette);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface4_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    ddraw_palette_handle = SelectPalette(dc, GetStockObject(DEFAULT_PALETTE), FALSE);
+    todo_wine ok(ddraw_palette_handle == GetStockObject(DEFAULT_PALETTE),
+            "Got unexpected palette %p, expected %p.\n",
+            ddraw_palette_handle, GetStockObject(DEFAULT_PALETTE));
+    SelectPalette(dc, ddraw_palette_handle, FALSE);
+
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected1) / sizeof(*expected1); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected1[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected1[i].rgbRed, expected1[i].rgbGreen, expected1[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+
+    /* Update the palette while the DC is in use. This does not modify the DC. */
+    palette_entries[4].peRed = 0x23;
+    palette_entries[4].peGreen = 0x24;
+    palette_entries[4].peBlue = 0x25;
+    hr = IDirectDrawPalette_SetEntries(palette, 0, 4, 1, &palette_entries[4]);
+    ok(SUCCEEDED(hr), "Failed to set palette entries, hr %#x.\n", hr);
+
+    i = GetDIBColorTable(dc, 4, 1, &rgbquad[4]);
+    ok(i == 1, "Expected count 1, got %u.\n", i);
+    todo_wine ok(!memcmp(&rgbquad[4], &expected1[4], sizeof(rgbquad[4])),
+            "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+            i, rgbquad[4].rgbRed, rgbquad[4].rgbGreen, rgbquad[4].rgbBlue,
+            expected1[4].rgbRed, expected1[4].rgbGreen, expected1[4].rgbBlue);
+
+    /* Neither does re-setting the palette. */
+    hr = IDirectDrawSurface4_SetPalette(surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface4_SetPalette(surface, palette);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+
+    i = GetDIBColorTable(dc, 4, 1, &rgbquad[4]);
+    ok(i == 1, "Expected count 1, got %u.\n", i);
+    todo_wine ok(!memcmp(&rgbquad[4], &expected1[4], sizeof(rgbquad[4])),
+            "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+            i, rgbquad[4].rgbRed, rgbquad[4].rgbGreen, rgbquad[4].rgbBlue,
+            expected1[4].rgbRed, expected1[4].rgbGreen, expected1[4].rgbBlue);
+
+    hr = IDirectDrawSurface4_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    /* Refresh the DC. This updates the palette. */
+    hr = IDirectDrawSurface4_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected2) / sizeof(*expected2); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected2[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected2[i].rgbRed, expected2[i].rgbGreen, expected2[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface4_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    refcount = IDirectDrawSurface4_Release(surface);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+
+    if (FAILED(hr = IDirectDraw4_SetDisplayMode(ddraw, 640, 480, 8, 0, 0)))
+    {
+        win_skip("Failed to set 8 bpp display mode, skipping test.\n");
+        IDirectDrawPalette_Release(palette);
+        IDirectDraw4_Release(ddraw);
+        DestroyWindow(window);
+        return;
+    }
+    ok(SUCCEEDED(hr), "Failed to set display mode, hr %#x.\n", hr);
+    hr = IDirectDraw4_SetCooperativeLevel(ddraw, window, DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE);
+    ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+    hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &primary, NULL);
+    ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+    hr = IDirectDrawSurface4_SetPalette(primary, palette);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface4_GetDC(primary, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    ddraw_palette_handle = SelectPalette(dc, GetStockObject(DEFAULT_PALETTE), FALSE);
+    /* Windows 2000 on the testbot assigns a different palette to the primary. Refrast? */
+    todo_wine ok(ddraw_palette_handle == GetStockObject(DEFAULT_PALETTE) || broken(TRUE),
+            "Got unexpected palette %p, expected %p.\n",
+            ddraw_palette_handle, GetStockObject(DEFAULT_PALETTE));
+    SelectPalette(dc, ddraw_palette_handle, FALSE);
+
+    /* The primary uses the system palette. In exclusive mode, the system palette matches
+     * the ddraw palette attached to the primary, so the result is what you would expect
+     * from a regular surface. Tests for the interaction between the ddraw palette and
+     * the system palette are not included pending an application that depends on this.
+     * The relation between those causes problems on Windows Vista and newer for games
+     * like Age of Empires or Starcraft. Don't emulate it without a real need. */
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected2) / sizeof(*expected2); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected2[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected2[i].rgbRed, expected2[i].rgbGreen, expected2[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface4_ReleaseDC(primary, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.dwWidth = 16;
+    surface_desc.dwHeight = 16;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+    /* Here the offscreen surface appears to use the primary's palette,
+     * but in all likelyhood it is actually the system palette. */
+    hr = IDirectDrawSurface4_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected2) / sizeof(*expected2); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected2[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected2[i].rgbRed, expected2[i].rgbGreen, expected2[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface4_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    /* On real hardware a change to the primary surface's palette applies immediately,
+     * even on device contexts from offscreen surfaces that do not have their own
+     * palette. On the testbot VMs this is not the case. Don't test this until we
+     * know of an application that depends on this. */
+
+    memset(palette_entries, 0, sizeof(palette_entries));
+    palette_entries[1].peBlue = 0x40;
+    palette_entries[2].peRed = 0x40;
+    palette_entries[3].peGreen = 0x40;
+    palette_entries[4].peRed = 0x12;
+    palette_entries[4].peGreen = 0x34;
+    palette_entries[4].peBlue = 0x56;
+    hr = IDirectDraw4_CreatePalette(ddraw, DDPCAPS_8BIT | DDPCAPS_ALLOW256,
+            palette_entries, &palette2, NULL);
+    ok(SUCCEEDED(hr), "Failed to create palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface4_SetPalette(surface, palette2);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+
+    /* A palette assigned to the offscreen surface overrides the primary / system
+     * palette. */
+    hr = IDirectDrawSurface4_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected3) / sizeof(*expected3); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected3[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected3[i].rgbRed, expected3[i].rgbGreen, expected3[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface4_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    /* Windows 8 does not properly release the palette when the
+     * surface is destroyed. Sigh. */
+    hr = IDirectDrawSurface4_SetPalette(primary, NULL);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+
+    refcount = IDirectDrawSurface4_Release(surface);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    /* See Win8 refcounting remarks in test_primary_palette. */
+    while (IDirectDrawSurface4_Release(primary));
+    refcount = IDirectDrawPalette_Release(palette2);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    refcount = IDirectDrawPalette_Release(palette);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    while (IDirectDraw4_Release(ddraw));
+    DestroyWindow(window);
+}
+
 START_TEST(ddraw4)
 {
     IDirectDraw4 *ddraw;
@@ -6841,4 +7139,5 @@ START_TEST(ddraw4)
     test_mipmap_lock();
     test_palette_complex();
     test_p8_rgb_blit();
+    test_palette_gdi();
 }
diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c
index d6f9d13..ceedc15 100644
--- a/dlls/ddraw/tests/ddraw7.c
+++ b/dlls/ddraw/tests/ddraw7.c
@@ -6667,6 +6667,304 @@ static void test_p8_rgb_blit(void)
     DestroyWindow(window);
 }
 
+static void test_palette_gdi(void)
+{
+    IDirectDrawSurface7 *surface, *primary;
+    DDSURFACEDESC2 surface_desc;
+    IDirectDraw7 *ddraw;
+    IDirectDrawPalette *palette, *palette2;
+    ULONG refcount;
+    HWND window;
+    HRESULT hr;
+    PALETTEENTRY palette_entries[256];
+    UINT i;
+    HDC dc;
+    /* On the Windows 8 testbot palette index 0 of the onscreen palette is forced to
+     * r = 0, g = 0, b = 0. Do not attempt to set it to something else as this is
+     * not the point of this test. */
+    static const RGBQUAD expected1[] =
+    {
+        {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x01, 0x00}, {0x00, 0x02, 0x00, 0x00},
+        {0x03, 0x00, 0x00, 0x00}, {0x15, 0x14, 0x13, 0x00},
+    };
+    static const RGBQUAD expected2[] =
+    {
+        {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x01, 0x00}, {0x00, 0x02, 0x00, 0x00},
+        {0x03, 0x00, 0x00, 0x00}, {0x25, 0x24, 0x23, 0x00},
+    };
+    static const RGBQUAD expected3[] =
+    {
+        {0x00, 0x00, 0x00, 0x00}, {0x40, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x40, 0x00},
+        {0x00, 0x40, 0x00, 0x00}, {0x56, 0x34, 0x12, 0x00},
+    };
+    HPALETTE ddraw_palette_handle;
+    /* Similar to index 0, index 255 is r = 0xff, g = 0xff, b = 0xff on the Win8 VMs. */
+    RGBQUAD rgbquad[255];
+    static const RGBQUAD rgb_zero = {0, 0, 0, 0};
+
+    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");
+    hr = IDirectDraw7_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
+    ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
+
+    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.dwWidth = 16;
+    surface_desc.dwHeight = 16;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    U4(surface_desc).ddpfPixelFormat.dwSize = sizeof(U4(surface_desc).ddpfPixelFormat);
+    U4(surface_desc).ddpfPixelFormat.dwFlags = DDPF_PALETTEINDEXED8 | DDPF_RGB;
+    U1(U4(surface_desc).ddpfPixelFormat).dwRGBBitCount = 8;
+    hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+    /* Avoid colors from the Windows default palette. */
+    memset(palette_entries, 0, sizeof(palette_entries));
+    palette_entries[1].peRed = 0x01;
+    palette_entries[2].peGreen = 0x02;
+    palette_entries[3].peBlue = 0x03;
+    palette_entries[4].peRed = 0x13;
+    palette_entries[4].peGreen = 0x14;
+    palette_entries[4].peBlue = 0x15;
+    hr = IDirectDraw7_CreatePalette(ddraw, DDPCAPS_8BIT | DDPCAPS_ALLOW256,
+            palette_entries, &palette, NULL);
+    ok(SUCCEEDED(hr), "Failed to create palette, hr %#x.\n", hr);
+
+    /* If there is no palette assigned and the display mode is not 8 bpp, some
+     * drivers refuse to create a DC while others allow it. If a DC is created,
+     * the DIB color table is uninitialized and contains random colors. No error
+     * is generated when trying to read pixels and random garbage is returned.
+     *
+     * The most likely explanation is that if the driver creates a DC, it (or
+     * the higher-level runtime) uses GetSystemPaletteEntries to find the
+     * palette, but GetSystemPaletteEntries fails when bpp > 8 and the palette
+     * contains uninitialized garbage. See comments beflow for the P8 case. */
+
+    hr = IDirectDrawSurface7_SetPalette(surface, palette);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface7_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    ddraw_palette_handle = SelectPalette(dc, GetStockObject(DEFAULT_PALETTE), FALSE);
+    todo_wine ok(ddraw_palette_handle == GetStockObject(DEFAULT_PALETTE),
+            "Got unexpected palette %p, expected %p.\n",
+            ddraw_palette_handle, GetStockObject(DEFAULT_PALETTE));
+    SelectPalette(dc, ddraw_palette_handle, FALSE);
+
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected1) / sizeof(*expected1); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected1[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected1[i].rgbRed, expected1[i].rgbGreen, expected1[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+
+    /* Update the palette while the DC is in use. This does not modify the DC. */
+    palette_entries[4].peRed = 0x23;
+    palette_entries[4].peGreen = 0x24;
+    palette_entries[4].peBlue = 0x25;
+    hr = IDirectDrawPalette_SetEntries(palette, 0, 4, 1, &palette_entries[4]);
+    ok(SUCCEEDED(hr), "Failed to set palette entries, hr %#x.\n", hr);
+
+    i = GetDIBColorTable(dc, 4, 1, &rgbquad[4]);
+    ok(i == 1, "Expected count 1, got %u.\n", i);
+    todo_wine ok(!memcmp(&rgbquad[4], &expected1[4], sizeof(rgbquad[4])),
+            "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+            i, rgbquad[4].rgbRed, rgbquad[4].rgbGreen, rgbquad[4].rgbBlue,
+            expected1[4].rgbRed, expected1[4].rgbGreen, expected1[4].rgbBlue);
+
+    /* Neither does re-setting the palette. */
+    hr = IDirectDrawSurface7_SetPalette(surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface7_SetPalette(surface, palette);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+
+    i = GetDIBColorTable(dc, 4, 1, &rgbquad[4]);
+    ok(i == 1, "Expected count 1, got %u.\n", i);
+    todo_wine ok(!memcmp(&rgbquad[4], &expected1[4], sizeof(rgbquad[4])),
+            "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+            i, rgbquad[4].rgbRed, rgbquad[4].rgbGreen, rgbquad[4].rgbBlue,
+            expected1[4].rgbRed, expected1[4].rgbGreen, expected1[4].rgbBlue);
+
+    hr = IDirectDrawSurface7_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    /* Refresh the DC. This updates the palette. */
+    hr = IDirectDrawSurface7_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected2) / sizeof(*expected2); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected2[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected2[i].rgbRed, expected2[i].rgbGreen, expected2[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface7_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    refcount = IDirectDrawSurface7_Release(surface);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+
+    if (FAILED(hr = IDirectDraw7_SetDisplayMode(ddraw, 640, 480, 8, 0, 0)))
+    {
+        win_skip("Failed to set 8 bpp display mode, skipping test.\n");
+        IDirectDrawPalette_Release(palette);
+        IDirectDraw7_Release(ddraw);
+        DestroyWindow(window);
+        return;
+    }
+    ok(SUCCEEDED(hr), "Failed to set display mode, hr %#x.\n", hr);
+    hr = IDirectDraw7_SetCooperativeLevel(ddraw, window, DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE);
+    ok(SUCCEEDED(hr), "Failed to set cooperative level, hr %#x.\n", hr);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+    hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &primary, NULL);
+    ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+    hr = IDirectDrawSurface7_SetPalette(primary, palette);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface7_GetDC(primary, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    ddraw_palette_handle = SelectPalette(dc, GetStockObject(DEFAULT_PALETTE), FALSE);
+    /* Windows 2000 on the testbot assigns a different palette to the primary. Refrast? */
+    todo_wine ok(ddraw_palette_handle == GetStockObject(DEFAULT_PALETTE) || broken(TRUE),
+            "Got unexpected palette %p, expected %p.\n",
+            ddraw_palette_handle, GetStockObject(DEFAULT_PALETTE));
+    SelectPalette(dc, ddraw_palette_handle, FALSE);
+
+    /* The primary uses the system palette. In exclusive mode, the system palette matches
+     * the ddraw palette attached to the primary, so the result is what you would expect
+     * from a regular surface. Tests for the interaction between the ddraw palette and
+     * the system palette are not included pending an application that depends on this.
+     * The relation between those causes problems on Windows Vista and newer for games
+     * like Age of Empires or Starcraft. Don't emulate it without a real need. */
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected2) / sizeof(*expected2); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected2[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected2[i].rgbRed, expected2[i].rgbGreen, expected2[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface7_ReleaseDC(primary, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    memset(&surface_desc, 0, sizeof(surface_desc));
+    surface_desc.dwSize = sizeof(surface_desc);
+    surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    surface_desc.dwWidth = 16;
+    surface_desc.dwHeight = 16;
+    surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+    hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &surface, NULL);
+    ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr);
+
+    /* Here the offscreen surface appears to use the primary's palette,
+     * but in all likelyhood it is actually the system palette. */
+    hr = IDirectDrawSurface7_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected2) / sizeof(*expected2); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected2[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected2[i].rgbRed, expected2[i].rgbGreen, expected2[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface7_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    /* On real hardware a change to the primary surface's palette applies immediately,
+     * even on device contexts from offscreen surfaces that do not have their own
+     * palette. On the testbot VMs this is not the case. Don't test this until we
+     * know of an application that depends on this. */
+
+    memset(palette_entries, 0, sizeof(palette_entries));
+    palette_entries[1].peBlue = 0x40;
+    palette_entries[2].peRed = 0x40;
+    palette_entries[3].peGreen = 0x40;
+    palette_entries[4].peRed = 0x12;
+    palette_entries[4].peGreen = 0x34;
+    palette_entries[4].peBlue = 0x56;
+    hr = IDirectDraw7_CreatePalette(ddraw, DDPCAPS_8BIT | DDPCAPS_ALLOW256,
+            palette_entries, &palette2, NULL);
+    ok(SUCCEEDED(hr), "Failed to create palette, hr %#x.\n", hr);
+    hr = IDirectDrawSurface7_SetPalette(surface, palette2);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+
+    /* A palette assigned to the offscreen surface overrides the primary / system
+     * palette. */
+    hr = IDirectDrawSurface7_GetDC(surface, &dc);
+    ok(SUCCEEDED(hr), "Failed to get DC, hr %#x.\n", hr);
+    i = GetDIBColorTable(dc, 0, sizeof(rgbquad) / sizeof(*rgbquad), rgbquad);
+    ok(i == sizeof(rgbquad) / sizeof(*rgbquad), "Expected count 255, got %u.\n", i);
+    for (i = 0; i < sizeof(expected3) / sizeof(*expected3); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &expected3[i], sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=%#x g=%#x b=%#x.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue,
+                expected3[i].rgbRed, expected3[i].rgbGreen, expected3[i].rgbBlue);
+    }
+    for (; i < sizeof(rgbquad) / sizeof(*rgbquad); i++)
+    {
+        ok(!memcmp(&rgbquad[i], &rgb_zero, sizeof(rgbquad[i])),
+                "Got color table entry %u r=%#x g=%#x b=%#x, expected r=0 g=0 b=0.\n",
+                i, rgbquad[i].rgbRed, rgbquad[i].rgbGreen, rgbquad[i].rgbBlue);
+    }
+    hr = IDirectDrawSurface7_ReleaseDC(surface, dc);
+    ok(SUCCEEDED(hr), "Failed to release DC, hr %#x.\n", hr);
+
+    /* Windows 8 does not properly release the palette when the
+     * surface is destroyed. Sigh. */
+    hr = IDirectDrawSurface7_SetPalette(primary, NULL);
+    ok(SUCCEEDED(hr), "Failed to set palette, hr %#x.\n", hr);
+
+    refcount = IDirectDrawSurface7_Release(surface);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    /* See Win8 refcounting remarks in test_primary_palette. */
+    while (IDirectDrawSurface7_Release(primary));
+    refcount = IDirectDrawPalette_Release(palette2);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    refcount = IDirectDrawPalette_Release(palette);
+    ok(!refcount, "Got unexpected refcount %u.\n", refcount);
+    while (IDirectDraw7_Release(ddraw));
+    DestroyWindow(window);
+}
+
 START_TEST(ddraw7)
 {
     HMODULE module = GetModuleHandleA("ddraw.dll");
@@ -6733,4 +7031,5 @@ START_TEST(ddraw7)
     test_mipmap_lock();
     test_palette_complex();
     test_p8_rgb_blit();
+    test_palette_gdi();
 }
diff --git a/dlls/ddraw/tests/visual.c b/dlls/ddraw/tests/visual.c
index 109c24c..0a54da7 100644
--- a/dlls/ddraw/tests/visual.c
+++ b/dlls/ddraw/tests/visual.c
@@ -2473,26 +2473,12 @@ static COLORREF getPixelColor_GDI(IDirectDrawSurface *Surface, UINT x, UINT y)
     return clr;
 }
 
-static BOOL colortables_check_equality(PALETTEENTRY table1[256], RGBQUAD table2[256])
-{
-    int i;
-
-    for (i = 0; i < 256; i++) {
-       if (table1[i].peRed != table2[i].rgbRed || table1[i].peGreen != table2[i].rgbGreen ||
-           table1[i].peBlue != table2[i].rgbBlue) return FALSE;
-    }
-
-    return TRUE;
-}
-
 static void p8_primary_test(void)
 {
     /* Test 8bit mode used by games like StarCraft, C&C Red Alert I etc */
     DDSURFACEDESC ddsd;
-    HDC hdc;
     HRESULT hr;
     PALETTEENTRY entries[256];
-    RGBQUAD coltable[256];
     UINT i, i1, i2;
     IDirectDrawPalette *ddprimpal = NULL;
     IDirectDrawSurface *offscreen = NULL;
@@ -2606,15 +2592,6 @@ static void p8_primary_test(void)
     hr = IDirectDrawPalette_SetEntries(ddprimpal, 0, 0, 256, entries);
     ok(hr == DD_OK, "IDirectDrawPalette_SetEntries failed with %08x\n", hr);
 
-    hr = IDirectDrawSurface_GetDC(offscreen, &hdc);
-    ok(hr==DD_OK, "IDirectDrawSurface_GetDC returned: %x\n", hr);
-    i = GetDIBColorTable(hdc, 0, 256, coltable);
-    ok(i == 256, "GetDIBColorTable returned %u, last error: %x\n", i, GetLastError());
-    hr = IDirectDrawSurface_ReleaseDC(offscreen, hdc);
-    ok(hr==DD_OK, "IDirectDrawSurface_ReleaseDC returned: %x\n", hr);
-
-    ok(colortables_check_equality(entries, coltable), "unexpected colortable on offscreen surface\n");
-
     p8_surface_fill_rect(offscreen, 0, 0, 16, 16, 2);
 
     memset(entries, 0, sizeof(entries));
-- 
1.8.3.2




More information about the wine-patches mailing list