From: Giovanni Mascellani wine@mascellani.eu
Signed-off-by: Giovanni Mascellani gmascellani@codeweavers.com Signed-off-by: Henri Verbeet hverbeet@codeweavers.com --- This supersedes patch 200966.
dlls/d2d1/d2d1.spec | 2 +- dlls/d2d1/d2d1_private.h | 7 +++ dlls/d2d1/factory.c | 77 +++++++++++++++++++++++++++++ dlls/d2d1/tests/d2d1.c | 102 +++++++++++++++++++++++++++++++++++++++ include/d2d1_1.idl | 2 + 5 files changed, 189 insertions(+), 1 deletion(-)
diff --git a/dlls/d2d1/d2d1.spec b/dlls/d2d1/d2d1.spec index 0ae894109fb2..3fa85a93bae9 100644 --- a/dlls/d2d1/d2d1.spec +++ b/dlls/d2d1/d2d1.spec @@ -3,7 +3,7 @@ @ stdcall D2D1MakeSkewMatrix(float float float float ptr) @ stdcall D2D1IsMatrixInvertible(ptr) @ stdcall D2D1InvertMatrix(ptr) -@ stub D2D1ConvertColorSpace +@ stdcall D2D1ConvertColorSpace(long long ptr) @ stdcall D2D1CreateDevice(ptr ptr ptr) @ stub D2D1CreateDeviceContext @ stdcall D2D1SinCos(float ptr ptr) diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index 4546d5c1e3a1..199439b46165 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -640,6 +640,13 @@ static inline D2D1_INTERPOLATION_MODE d2d1_1_interp_mode_from_d2d1(D2D1_BITMAP_I return (D2D1_INTERPOLATION_MODE)mode; }
+static inline const char *debug_d2d_color_f(const D2D1_COLOR_F *colour) +{ + if (!colour) + return "(null)"; + return wine_dbg_sprintf("{%.8e, %.8e, %.8e, %.8e}", colour->r, colour->g, colour->b, colour->a); +} + static inline const char *debug_d2d_point_2f(const D2D1_POINT_2F *point) { if (!point) diff --git a/dlls/d2d1/factory.c b/dlls/d2d1/factory.c index 6b6eab766c7b..2a9de448c6ec 100644 --- a/dlls/d2d1/factory.c +++ b/dlls/d2d1/factory.c @@ -735,6 +735,83 @@ float WINAPI D2D1Vec3Length(float x, float y, float z) return sqrtf(x * x + y * y + z * z); }
+/* See IEC 61966-2-1:1999; also described in the EXT_texture_sRGB OpenGL + * extension, among others. */ +static float srgb_transfer_function(float x) +{ + if (x <= 0.0f) + return 0.0f; + else if (x >= 1.0f) + return 1.0f; + else if (x <= 0.0031308f) + return 12.92f * x; + else + return 1.055f * powf(x, 1.0f / 2.4f) - 0.055f; +} + +static float srgb_inverse_transfer_function(float x) +{ + if (x <= 0.0f) + return 0.0f; + else if (x >= 1.0f) + return 1.0f; + else if (x <= 0.04045f) + return x / 12.92f; + else + return powf((x + 0.055f) / 1.055f, 2.4f); +} + +D2D1_COLOR_F WINAPI D2D1ConvertColorSpace(D2D1_COLOR_SPACE src_colour_space, + D2D1_COLOR_SPACE dst_colour_space, const D2D1_COLOR_F *colour) +{ + D2D1_COLOR_F ret; + + TRACE("src_colour_space %#x, dst_colour_space %#x, colour %s.\n", + src_colour_space, dst_colour_space, debug_d2d_color_f(colour)); + + if (src_colour_space == D2D1_COLOR_SPACE_CUSTOM || dst_colour_space == D2D1_COLOR_SPACE_CUSTOM) + { + ret.r = 0.0f; + ret.g = 0.0f; + ret.b = 0.0f; + ret.a = 0.0f; + + return ret; + } + + if (src_colour_space == dst_colour_space) + return *colour; + + if (src_colour_space == D2D1_COLOR_SPACE_SRGB && dst_colour_space == D2D1_COLOR_SPACE_SCRGB) + { + ret.r = srgb_inverse_transfer_function(colour->r); + ret.g = srgb_inverse_transfer_function(colour->g); + ret.b = srgb_inverse_transfer_function(colour->b); + ret.a = colour->a; + + return ret; + } + + if (src_colour_space == D2D1_COLOR_SPACE_SCRGB && dst_colour_space == D2D1_COLOR_SPACE_SRGB) + { + ret.r = srgb_transfer_function(colour->r); + ret.g = srgb_transfer_function(colour->g); + ret.b = srgb_transfer_function(colour->b); + ret.a = colour->a; + + return ret; + } + + FIXME("Unhandled conversion from source colour space %#x to destination colour space %#x.\n", + src_colour_space, dst_colour_space); + ret.r = 0.0f; + ret.g = 0.0f; + ret.b = 0.0f; + ret.a = 0.0f; + + return ret; +} + static BOOL get_config_key_dword(HKEY default_key, HKEY application_key, const char *name, DWORD *value) { DWORD type, data, size; diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c index 8d5846058198..1dc7a27ca5bd 100644 --- a/dlls/d2d1/tests/d2d1.c +++ b/dlls/d2d1/tests/d2d1.c @@ -34,6 +34,8 @@ static HRESULT (WINAPI *pD2D1CreateDevice)(IDXGIDevice *dxgi_device, static void (WINAPI *pD2D1SinCos)(float angle, float *s, float *c); static float (WINAPI *pD2D1Tan)(float angle); static float (WINAPI *pD2D1Vec3Length)(float x, float y, float z); +static D2D1_COLOR_F (WINAPI *pD2D1ConvertColorSpace)(D2D1_COLOR_SPACE src_colour_space, + D2D1_COLOR_SPACE dst_colour_space, const D2D1_COLOR_F *colour);
static BOOL use_mt = TRUE;
@@ -461,6 +463,11 @@ static DWORD get_readback_colour(struct resource_readback *rb, unsigned int x, u return ((DWORD *)((BYTE *)rb->data + y * rb->pitch))[x]; }
+static float clamp_float(float f, float lower, float upper) +{ + return f < lower ? lower : f > upper ? upper : f; +} + static BOOL compare_uint(unsigned int x, unsigned int y, unsigned int max_diff) { unsigned int diff = x > y ? x - y : y - x; @@ -492,6 +499,14 @@ static BOOL compare_float(float f, float g, unsigned int ulps) return TRUE; }
+static BOOL compare_colour_f(const D2D1_COLOR_F *colour, float r, float g, float b, float a, unsigned int ulps) +{ + return compare_float(colour->r, r, ulps) + && compare_float(colour->g, g, ulps) + && compare_float(colour->b, b, ulps) + && compare_float(colour->a, a, ulps); +} + static BOOL compare_point(const D2D1_POINT_2F *point, float x, float y, unsigned int ulps) { return compare_float(point->x, x, ulps) @@ -9466,6 +9481,91 @@ static void test_math(BOOL d3d11) } }
+static void test_colour_space(BOOL d3d11) +{ + D2D1_COLOR_F src_colour, dst_colour, expected; + D2D1_COLOR_SPACE src_space, dst_space; + unsigned i, j, k; + + static const D2D1_COLOR_SPACE colour_spaces[] = + { + D2D1_COLOR_SPACE_CUSTOM, + D2D1_COLOR_SPACE_SRGB, + D2D1_COLOR_SPACE_SCRGB, + }; + static struct + { + D2D1_COLOR_F srgb; + D2D1_COLOR_F scrgb; + } + const test_data[] = + { + {{0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}}, + {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 1.0f}}, + {{1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, + /* Samples in the non-linear region. */ + {{0.2f, 0.4f, 0.6f, 0.8f}, {0.0331047624f, 0.132868335f, 0.318546832f, 0.8f}}, + {{0.3f, 0.5f, 0.7f, 0.9f}, {0.0732389688f, 0.214041144f, 0.447988421f, 0.9f}}, + /* Samples in the linear region. */ + {{0.0002f, 0.0004f, 0.0006f, 0.0008f}, {1.54798763e-005f, 3.09597526e-005f, 4.64396289e-005f, 0.0008f}}, + {{0.0003f, 0.0005f, 0.0007f, 0.0009f}, {2.32198145e-005f, 3.86996908e-005f, 5.41795634e-005f, 0.0009f}}, + /* Out of range samples */ + {{-0.3f, 1.5f, -0.7f, 2.0f}, { 0.0f, 1.0f, 0.0f, 2.0f}}, + {{ 1.5f, -0.3f, 2.0f, -0.7f}, { 1.0f, 0.0f, 1.0f, -0.7f}}, + {{ 0.0f, 1.0f, 0.0f, 1.5f}, {-0.7f, 2.0f, -0.3f, 1.5f}}, + {{ 1.0f, 0.0f, 1.0f, -0.3f}, { 2.0f, -0.7f, 1.5f, -0.3f}}, + }; + + if (!pD2D1ConvertColorSpace) + { + win_skip("D2D1ConvertColorSpace() not available, skipping test.\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(colour_spaces); ++i) + { + src_space = colour_spaces[i]; + for (j = 0; j < ARRAY_SIZE(colour_spaces); ++j) + { + dst_space = colour_spaces[j]; + for (k = 0; k < ARRAY_SIZE(test_data); ++k) + { + if (src_space == D2D1_COLOR_SPACE_SCRGB) + src_colour = test_data[k].scrgb; + else + src_colour = test_data[k].srgb; + + if (dst_space == D2D1_COLOR_SPACE_SCRGB) + expected = test_data[k].scrgb; + else + expected = test_data[k].srgb; + + if (src_space == D2D1_COLOR_SPACE_CUSTOM || dst_space == D2D1_COLOR_SPACE_CUSTOM) + { + set_color(&expected, 0.0f, 0.0f, 0.0f, 0.0f); + } + else if (src_space != dst_space) + { + expected.r = clamp_float(expected.r, 0.0f, 1.0f); + expected.g = clamp_float(expected.g, 0.0f, 1.0f); + expected.b = clamp_float(expected.b, 0.0f, 1.0f); + } + + dst_colour = pD2D1ConvertColorSpace(src_space, dst_space, &src_colour); + ok(compare_colour_f(&dst_colour, expected.r, expected.g, expected.b, expected.a, 1), + "Got unexpected destination colour {%.8e, %.8e, %.8e, %.8e}, " + "expected destination colour {%.8e, %.8e, %.8e, %.8e} for " + "source colour {%.8e, %.8e, %.8e, %.8e}, " + "source colour space %#x, destination colour space %#x.\n", + dst_colour.r, dst_colour.g, dst_colour.b, dst_colour.a, + expected.r, expected.g, expected.b, expected.a, + src_colour.r, src_colour.g, src_colour.b, src_colour.a, + src_space, dst_space); + } + } + } +} + START_TEST(d2d1) { HMODULE d2d1_dll = GetModuleHandleA("d2d1.dll"); @@ -9476,6 +9576,7 @@ START_TEST(d2d1) pD2D1SinCos = (void *)GetProcAddress(d2d1_dll, "D2D1SinCos"); pD2D1Tan = (void *)GetProcAddress(d2d1_dll, "D2D1Tan"); pD2D1Vec3Length = (void *)GetProcAddress(d2d1_dll, "D2D1Vec3Length"); + pD2D1ConvertColorSpace = (void *)GetProcAddress(d2d1_dll, "D2D1ConvertColorSpace");
use_mt = !getenv("WINETEST_NO_MT_D3D");
@@ -9523,6 +9624,7 @@ START_TEST(d2d1) queue_test(test_dpi); queue_test(test_wic_bitmap_format); queue_d3d10_test(test_math); + queue_d3d10_test(test_colour_space);
run_queued_tests(); } diff --git a/include/d2d1_1.idl b/include/d2d1_1.idl index 1bcadab01d53..257a970968f8 100644 --- a/include/d2d1_1.idl +++ b/include/d2d1_1.idl @@ -962,3 +962,5 @@ interface ID2D1Multithread : IUnknown [local] void __stdcall D2D1SinCos(float angle, float *s, float *c); [local] float __stdcall D2D1Tan(float angle); [local] float __stdcall D2D1Vec3Length(float x, float y, float z); +[local] D2D1_COLOR_F __stdcall D2D1ConvertColorSpace(D2D1_COLOR_SPACE src_colour_space, + D2D1_COLOR_SPACE dst_colour_space, const D2D1_COLOR_F *colour);