[PATCH 4/5] d2d1: Implement linear gradient brushes.

Henri Verbeet hverbeet at codeweavers.com
Tue Sep 26 05:22:39 CDT 2017


Signed-off-by: Henri Verbeet <hverbeet at codeweavers.com>
---
 dlls/d2d1/brush.c         |  60 +++++++++---
 dlls/d2d1/d2d1_private.h  |  17 +++-
 dlls/d2d1/render_target.c | 213 +++++++++++++++++++++++++++++++++----------
 dlls/d2d1/tests/d2d1.c    | 228 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 451 insertions(+), 67 deletions(-)

diff --git a/dlls/d2d1/brush.c b/dlls/d2d1/brush.c
index 1fc00e9..d988c51 100644
--- a/dlls/d2d1/brush.c
+++ b/dlls/d2d1/brush.c
@@ -217,6 +217,14 @@ HRESULT d2d_gradient_create(ID2D1Factory *factory, ID3D10Device *device, const D
     return S_OK;
 }
 
+static struct d2d_gradient *unsafe_impl_from_ID2D1GradientStopCollection(ID2D1GradientStopCollection *iface)
+{
+    if (!iface)
+        return NULL;
+    assert(iface->lpVtbl == &d2d_gradient_vtbl);
+    return CONTAINING_RECORD(iface, struct d2d_gradient, ID2D1GradientStopCollection_iface);
+}
+
 static void d2d_brush_destroy(struct d2d_brush *brush)
 {
     ID2D1Factory_Release(brush->factory);
@@ -429,7 +437,7 @@ static ULONG STDMETHODCALLTYPE d2d_linear_gradient_brush_Release(ID2D1LinearGrad
 
     if (!refcount)
     {
-        ID2D1GradientStopCollection_Release(brush->u.linear.gradient);
+        ID2D1GradientStopCollection_Release(&brush->u.linear.gradient->ID2D1GradientStopCollection_iface);
         d2d_brush_destroy(brush);
     }
 
@@ -491,7 +499,7 @@ static void STDMETHODCALLTYPE d2d_linear_gradient_brush_SetStartPoint(ID2D1Linea
 
     TRACE("iface %p, start_point {%.8e, %.8e}.\n", iface, start_point.x, start_point.y);
 
-    brush->u.linear.desc.startPoint = start_point;
+    brush->u.linear.start = start_point;
 }
 
 static void STDMETHODCALLTYPE d2d_linear_gradient_brush_SetEndPoint(ID2D1LinearGradientBrush *iface,
@@ -501,7 +509,7 @@ static void STDMETHODCALLTYPE d2d_linear_gradient_brush_SetEndPoint(ID2D1LinearG
 
     TRACE("iface %p, end_point {%.8e, %.8e}.\n", iface, end_point.x, end_point.y);
 
-    brush->u.linear.desc.endPoint = end_point;
+    brush->u.linear.end = end_point;
 }
 
 static D2D1_POINT_2F * STDMETHODCALLTYPE d2d_linear_gradient_brush_GetStartPoint(ID2D1LinearGradientBrush *iface,
@@ -511,7 +519,7 @@ static D2D1_POINT_2F * STDMETHODCALLTYPE d2d_linear_gradient_brush_GetStartPoint
 
     TRACE("iface %p, point %p.\n", iface, point);
 
-    *point = brush->u.linear.desc.startPoint;
+    *point = brush->u.linear.start;
     return point;
 }
 
@@ -522,7 +530,7 @@ static D2D1_POINT_2F * STDMETHODCALLTYPE d2d_linear_gradient_brush_GetEndPoint(I
 
     TRACE("iface %p, point %p.\n", iface, point);
 
-    *point = brush->u.linear.desc.endPoint;
+    *point = brush->u.linear.end;
     return point;
 }
 
@@ -533,7 +541,7 @@ static void STDMETHODCALLTYPE d2d_linear_gradient_brush_GetGradientStopCollectio
 
     TRACE("iface %p, gradient %p.\n", iface, gradient);
 
-    ID2D1GradientStopCollection_AddRef(*gradient = brush->u.linear.gradient);
+    ID2D1GradientStopCollection_AddRef(*gradient = &brush->u.linear.gradient->ID2D1GradientStopCollection_iface);
 }
 
 static const struct ID2D1LinearGradientBrushVtbl d2d_linear_gradient_brush_vtbl =
@@ -561,8 +569,10 @@ HRESULT d2d_linear_gradient_brush_create(ID2D1Factory *factory, const D2D1_LINEA
 
     d2d_brush_init(*brush, factory, D2D_BRUSH_TYPE_LINEAR, brush_desc,
             (ID2D1BrushVtbl *)&d2d_linear_gradient_brush_vtbl);
-    (*brush)->u.linear.desc = *gradient_brush_desc;
-    ID2D1GradientStopCollection_AddRef((*brush)->u.linear.gradient = gradient);
+    (*brush)->u.linear.gradient = unsafe_impl_from_ID2D1GradientStopCollection(gradient);
+    ID2D1GradientStopCollection_AddRef(&(*brush)->u.linear.gradient->ID2D1GradientStopCollection_iface);
+    (*brush)->u.linear.start = gradient_brush_desc->startPoint;
+    (*brush)->u.linear.end = gradient_brush_desc->endPoint;
 
     TRACE("Created brush %p.\n", *brush);
     return S_OK;
@@ -866,6 +876,14 @@ static BOOL d2d_brush_fill_cb(const struct d2d_brush *brush,
 
             return TRUE;
 
+        case D2D_BRUSH_TYPE_LINEAR:
+            b = brush->transform;
+            d2d_point_transform(&cb->u.linear.start, &b, brush->u.linear.start.x, brush->u.linear.start.y);
+            d2d_point_transform(&cb->u.linear.end, &b, brush->u.linear.end.x, brush->u.linear.end.y);
+            cb->u.linear.stop_count = brush->u.linear.gradient->stop_count;
+
+            return TRUE;
+
         case D2D_BRUSH_TYPE_BITMAP:
             bitmap = brush->u.bitmap.bitmap;
 
@@ -932,6 +950,11 @@ HRESULT d2d_brush_get_ps_cb(struct d2d_brush *brush, struct d2d_brush *opacity_b
     return hr;
 }
 
+static void d2d_brush_bind_gradient(struct d2d_brush *brush, ID3D10Device *device, unsigned int brush_idx)
+{
+    ID3D10Device_PSSetShaderResources(device, 2 + brush_idx, 1, &brush->u.linear.gradient->view);
+}
+
 static void d2d_brush_bind_bitmap(struct d2d_brush *brush, ID3D10Device *device, unsigned int brush_idx)
 {
     HRESULT hr;
@@ -967,8 +990,21 @@ static void d2d_brush_bind_bitmap(struct d2d_brush *brush, ID3D10Device *device,
 
 void d2d_brush_bind_resources(struct d2d_brush *brush, ID3D10Device *device, unsigned int brush_idx)
 {
-    if (brush->type == D2D_BRUSH_TYPE_BITMAP)
-        d2d_brush_bind_bitmap(brush, device, brush_idx);
-    else if (brush->type != D2D_BRUSH_TYPE_SOLID)
-        FIXME("Unhandled brush type %#x.\n", brush->type);
+    switch (brush->type)
+    {
+        case D2D_BRUSH_TYPE_SOLID:
+            break;
+
+        case D2D_BRUSH_TYPE_LINEAR:
+            d2d_brush_bind_gradient(brush, device, brush_idx);
+            break;
+
+        case D2D_BRUSH_TYPE_BITMAP:
+            d2d_brush_bind_bitmap(brush, device, brush_idx);
+            break;
+
+        default:
+            FIXME("Unhandled brush type %#x.\n", brush->type);
+            break;
+    }
 }
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h
index efb504a..cde1a32 100644
--- a/dlls/d2d1/d2d1_private.h
+++ b/dlls/d2d1/d2d1_private.h
@@ -83,6 +83,12 @@ struct d2d_brush_cb
         } solid;
         struct
         {
+            D2D1_POINT_2F start;
+            D2D1_POINT_2F end;
+            unsigned int stop_count;
+        } linear;
+        struct
+        {
             float _11, _21, _31, pad;
             float _12, _22, _32;
             BOOL ignore_alpha;
@@ -228,17 +234,18 @@ struct d2d_brush
         } solid;
         struct
         {
+            struct d2d_gradient *gradient;
+            D2D1_POINT_2F start;
+            D2D1_POINT_2F end;
+        } linear;
+        struct
+        {
             struct d2d_bitmap *bitmap;
             D2D1_EXTEND_MODE extend_mode_x;
             D2D1_EXTEND_MODE extend_mode_y;
             D2D1_BITMAP_INTERPOLATION_MODE interpolation_mode;
             ID3D10SamplerState *sampler_state;
         } bitmap;
-        struct
-        {
-            D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES desc;
-            ID2D1GradientStopCollection *gradient;
-        } linear;
     } u;
 };
 
diff --git a/dlls/d2d1/render_target.c b/dlls/d2d1/render_target.c
index 98471cb..431c4e8 100644
--- a/dlls/d2d1/render_target.c
+++ b/dlls/d2d1/render_target.c
@@ -2578,6 +2578,7 @@ static HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_t
 
         SamplerState s0, s1;
         Texture2D t0, t1;
+        Buffer<float4> b0, b1;
 
         struct input
         {
@@ -2586,6 +2587,43 @@ static HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_t
             nointerpolation float2x2 stroke_transform : STROKE_TRANSFORM;
         };
 
+        float4 brush_linear(struct brush brush, Buffer<float4> b, float2 position)
+        {
+            float2 start, end, v_p, v_q;
+            float p, p_low, p_high;
+            float4 c_low, c_high;
+            uint stop_count, i;
+
+            start = brush.data[0].xy;
+            end = brush.data[0].zw;
+            stop_count = asuint(brush.data[1].x);
+
+            v_p = position - start;
+            v_q = end - start;
+            p = dot(v_q, v_p) / dot(v_q, v_q);
+
+            p_low = b.Load(0).x;
+            c_low = b.Load(1);
+            c_high = c_low;
+
+            if (p < p_low)
+                return c_low;
+
+            for (i = 1; i < stop_count; ++i)
+            {
+                p_high = b.Load(i * 2).x;
+                c_high = b.Load(i * 2 + 1);
+
+                if (p >= p_low && p <= p_high)
+                    return lerp(c_low, c_high, (p - p_low) / (p_high - p_low));
+
+                p_low = p_high;
+                c_low = c_high;
+            }
+
+            return c_high;
+        }
+
         float4 brush_bitmap(struct brush brush, Texture2D t, SamplerState s, float2 position)
         {
             float3 transform[2];
@@ -2605,10 +2643,12 @@ static HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_t
             return colour;
         }
 
-        float4 sample_brush(struct brush brush, Texture2D t, SamplerState s, float2 position)
+        float4 sample_brush(struct brush brush, Texture2D t, SamplerState s, Buffer<float4> b, float2 position)
         {
             if (brush.type == BRUSH_TYPE_SOLID)
                 return brush.data[0] * brush.opacity;
+            if (brush.type == BRUSH_TYPE_LINEAR)
+                return brush_linear(brush, b, position) * brush.opacity;
             if (brush.type == BRUSH_TYPE_BITMAP)
                 return brush_bitmap(brush, t, s, position) * brush.opacity;
             return float4(0.0, 0.0, 0.0, brush.opacity);
@@ -2618,9 +2658,9 @@ static HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_t
         {
             float4 colour;
 
-            colour = sample_brush(colour_brush, t0, s0, i.p);
+            colour = sample_brush(colour_brush, t0, s0, b0, i.p);
             if (opacity_brush.type < BRUSH_TYPE_COUNT)
-                colour *= sample_brush(opacity_brush, t1, s1, i.p).a;
+                colour *= sample_brush(opacity_brush, t1, s1, b1, i.p).a;
 
             if (outline)
             {
@@ -2654,66 +2694,139 @@ static HRESULT d2d_d3d_render_target_init(struct d2d_d3d_render_target *render_t
             return colour;
         }
 #endif
-        0x43425844, 0x52e10ec7, 0xa7dbaa25, 0xb255a016, 0xf301e644, 0x00000001, 0x00000778, 0x00000003,
+        0x43425844, 0x8b6abf47, 0x898d4cd7, 0x0052859f, 0x209737c6, 0x00000001, 0x00001094, 0x00000003,
         0x0000002c, 0x000000c4, 0x000000f8, 0x4e475349, 0x00000090, 0x00000004, 0x00000008, 0x00000068,
         0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000303, 0x00000077, 0x00000000, 0x00000000,
         0x00000003, 0x00000001, 0x00000f0f, 0x0000007e, 0x00000000, 0x00000000, 0x00000003, 0x00000002,
         0x00000303, 0x0000007e, 0x00000001, 0x00000000, 0x00000003, 0x00000003, 0x00000303, 0x4c524f57,
         0x4f505f44, 0x49544953, 0x42004e4f, 0x45495a45, 0x54530052, 0x454b4f52, 0x4152545f, 0x4f46534e,
         0xab004d52, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000,
-        0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000678,
-        0x00000040, 0x0000019e, 0x04000059, 0x00208e46, 0x00000000, 0x00000007, 0x0300005a, 0x00106000,
+        0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000f94,
+        0x00000040, 0x000003e5, 0x04000059, 0x00208e46, 0x00000000, 0x00000007, 0x0300005a, 0x00106000,
         0x00000000, 0x0300005a, 0x00106000, 0x00000001, 0x04001858, 0x00107000, 0x00000000, 0x00005555,
-        0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x03001062, 0x00101032, 0x00000000, 0x03001062,
+        0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x04000858, 0x00107000, 0x00000002, 0x00005555,
+        0x04000858, 0x00107000, 0x00000003, 0x00005555, 0x03001062, 0x00101032, 0x00000000, 0x03001062,
         0x001010f2, 0x00000001, 0x03000862, 0x00101032, 0x00000002, 0x03000862, 0x00101032, 0x00000003,
-        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000003, 0x09000038, 0x001000f2, 0x00000000,
+        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x0000000a, 0x09000038, 0x001000f2, 0x00000000,
         0x00208556, 0x00000000, 0x00000001, 0x00208e46, 0x00000000, 0x00000002, 0x0404001f, 0x0020800a,
         0x00000000, 0x00000001, 0x08000020, 0x00100012, 0x00000001, 0x0020800a, 0x00000000, 0x00000001,
-        0x00004001, 0x00000002, 0x0304001f, 0x0010000a, 0x00000001, 0x0800000f, 0x00100022, 0x00000001,
-        0x00101046, 0x00000000, 0x00208046, 0x00000000, 0x00000002, 0x08000000, 0x00100012, 0x00000002,
-        0x0010001a, 0x00000001, 0x0020802a, 0x00000000, 0x00000002, 0x0800000f, 0x00100022, 0x00000001,
-        0x00101046, 0x00000000, 0x00208046, 0x00000000, 0x00000003, 0x08000000, 0x00100022, 0x00000002,
-        0x0010001a, 0x00000001, 0x0020802a, 0x00000000, 0x00000003, 0x09000045, 0x001000f2, 0x00000002,
-        0x00100046, 0x00000002, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x0a000037, 0x00100082,
-        0x00000002, 0x0020803a, 0x00000000, 0x00000003, 0x00004001, 0x3f800000, 0x0010003a, 0x00000002,
-        0x08000038, 0x001000f2, 0x00000000, 0x00100e46, 0x00000002, 0x00208556, 0x00000000, 0x00000001,
-        0x01000015, 0x05000036, 0x00100012, 0x00000002, 0x00004001, 0x00000000, 0x06000036, 0x00100082,
-        0x00000002, 0x0020801a, 0x00000000, 0x00000001, 0x09000037, 0x001000f2, 0x00000000, 0x00100006,
-        0x00000001, 0x00100e46, 0x00000000, 0x00100c06, 0x00000002, 0x01000015, 0x0800004f, 0x00100012,
+        0x00004001, 0x00000001, 0x0304001f, 0x0010000a, 0x00000001, 0x09000000, 0x00100062, 0x00000001,
+        0x00101106, 0x00000000, 0x80208106, 0x00000041, 0x00000000, 0x00000002, 0x0a000000, 0x00100032,
+        0x00000002, 0x80208046, 0x00000041, 0x00000000, 0x00000002, 0x00208ae6, 0x00000000, 0x00000002,
+        0x0700000f, 0x00100022, 0x00000001, 0x00100046, 0x00000002, 0x00100596, 0x00000001, 0x0700000f,
+        0x00100042, 0x00000001, 0x00100046, 0x00000002, 0x00100046, 0x00000002, 0x0700000e, 0x00100022,
+        0x00000001, 0x0010001a, 0x00000001, 0x0010002a, 0x00000001, 0x0a00002d, 0x001000f2, 0x00000002,
+        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00107e46, 0x00000002, 0x0a00002d,
+        0x001000f2, 0x00000003, 0x00004002, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00107e46,
+        0x00000002, 0x0700001d, 0x00100042, 0x00000001, 0x0010001a, 0x00000001, 0x0010000a, 0x00000002,
+        0x0304001f, 0x0010002a, 0x00000001, 0x05000036, 0x001000f2, 0x00000004, 0x00100e46, 0x00000003,
+        0x05000036, 0x001000f2, 0x00000005, 0x00100e46, 0x00000003, 0x05000036, 0x001000f2, 0x00000006,
+        0x00100e46, 0x00000003, 0x05000036, 0x00100042, 0x00000001, 0x0010000a, 0x00000002, 0x05000036,
+        0x00100082, 0x00000001, 0x00004001, 0x00000001, 0x05000036, 0x00100022, 0x00000002, 0x00004001,
+        0x00000000, 0x01000030, 0x08000050, 0x00100042, 0x00000002, 0x0010003a, 0x00000001, 0x0020800a,
+        0x00000000, 0x00000003, 0x05000036, 0x00100022, 0x00000002, 0x00004001, 0x00000000, 0x03040003,
+        0x0010002a, 0x00000002, 0x07000029, 0x00100042, 0x00000002, 0x0010003a, 0x00000001, 0x00004001,
+        0x00000001, 0x0700002d, 0x001000f2, 0x00000007, 0x00100aa6, 0x00000002, 0x00107e46, 0x00000002,
+        0x0700001e, 0x00100042, 0x00000002, 0x0010002a, 0x00000002, 0x00004001, 0x00000001, 0x0700002d,
+        0x001000f2, 0x00000008, 0x00100aa6, 0x00000002, 0x00107e46, 0x00000002, 0x0700001d, 0x00100042,
+        0x00000002, 0x0010001a, 0x00000001, 0x0010002a, 0x00000001, 0x0700001d, 0x00100082, 0x00000002,
+        0x0010000a, 0x00000007, 0x0010001a, 0x00000001, 0x07000001, 0x00100042, 0x00000002, 0x0010003a,
+        0x00000002, 0x0010002a, 0x00000002, 0x0304001f, 0x0010002a, 0x00000002, 0x08000000, 0x00100082,
+        0x00000002, 0x8010002a, 0x00000041, 0x00000001, 0x0010001a, 0x00000001, 0x08000000, 0x00100022,
+        0x00000007, 0x8010002a, 0x00000041, 0x00000001, 0x0010000a, 0x00000007, 0x0700000e, 0x00100082,
+        0x00000002, 0x0010003a, 0x00000002, 0x0010001a, 0x00000007, 0x08000000, 0x001000f2, 0x00000009,
+        0x80100e46, 0x00000041, 0x00000005, 0x00100e46, 0x00000008, 0x09000032, 0x001000f2, 0x00000009,
+        0x00100ff6, 0x00000002, 0x00100e46, 0x00000009, 0x00100e46, 0x00000005, 0x05000036, 0x001000f2,
+        0x00000006, 0x00100e46, 0x00000008, 0x05000036, 0x00100022, 0x00000002, 0x00004001, 0xffffffff,
+        0x05000036, 0x001000f2, 0x00000004, 0x00100e46, 0x00000009, 0x01000002, 0x01000015, 0x05000036,
+        0x001000f2, 0x00000005, 0x00100e46, 0x00000008, 0x05000036, 0x00100042, 0x00000001, 0x0010000a,
+        0x00000007, 0x0700001e, 0x00100082, 0x00000001, 0x0010003a, 0x00000001, 0x00004001, 0x00000001,
+        0x05000036, 0x001000f2, 0x00000006, 0x00100e46, 0x00000008, 0x05000036, 0x00100022, 0x00000002,
+        0x0010002a, 0x00000002, 0x01000016, 0x09000037, 0x001000f2, 0x00000003, 0x00100556, 0x00000002,
+        0x00100e46, 0x00000004, 0x00100e46, 0x00000006, 0x01000015, 0x08000038, 0x001000f2, 0x00000000,
+        0x00100e46, 0x00000003, 0x00208556, 0x00000000, 0x00000001, 0x01000015, 0x0300001f, 0x0010000a,
+        0x00000001, 0x08000020, 0x00100012, 0x00000001, 0x0020800a, 0x00000000, 0x00000001, 0x00004001,
+        0x00000002, 0x0304001f, 0x0010000a, 0x00000001, 0x0800000f, 0x00100022, 0x00000001, 0x00101046,
+        0x00000000, 0x00208046, 0x00000000, 0x00000002, 0x08000000, 0x00100012, 0x00000002, 0x0010001a,
+        0x00000001, 0x0020802a, 0x00000000, 0x00000002, 0x0800000f, 0x00100022, 0x00000001, 0x00101046,
+        0x00000000, 0x00208046, 0x00000000, 0x00000003, 0x08000000, 0x00100022, 0x00000002, 0x0010001a,
+        0x00000001, 0x0020802a, 0x00000000, 0x00000003, 0x09000045, 0x001000f2, 0x00000002, 0x00100046,
+        0x00000002, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x0a000037, 0x00100082, 0x00000002,
+        0x0020803a, 0x00000000, 0x00000003, 0x00004001, 0x3f800000, 0x0010003a, 0x00000002, 0x08000038,
+        0x001000f2, 0x00000000, 0x00100e46, 0x00000002, 0x00208556, 0x00000000, 0x00000001, 0x01000015,
+        0x05000036, 0x00100012, 0x00000002, 0x00004001, 0x00000000, 0x06000036, 0x00100082, 0x00000002,
+        0x0020801a, 0x00000000, 0x00000001, 0x09000037, 0x001000f2, 0x00000000, 0x00100006, 0x00000001,
+        0x00100e46, 0x00000000, 0x00100c06, 0x00000002, 0x01000015, 0x01000015, 0x0800004f, 0x00100012,
         0x00000001, 0x0020800a, 0x00000000, 0x00000004, 0x00004001, 0x00000003, 0x0304001f, 0x0010000a,
         0x00000001, 0x09000038, 0x00100012, 0x00000001, 0x0020801a, 0x00000000, 0x00000004, 0x0020803a,
         0x00000000, 0x00000005, 0x0404001f, 0x0020800a, 0x00000000, 0x00000004, 0x08000020, 0x00100022,
-        0x00000001, 0x0020800a, 0x00000000, 0x00000004, 0x00004001, 0x00000002, 0x0304001f, 0x0010001a,
-        0x00000001, 0x0800000f, 0x00100042, 0x00000001, 0x00101046, 0x00000000, 0x00208046, 0x00000000,
-        0x00000005, 0x08000000, 0x00100012, 0x00000002, 0x0010002a, 0x00000001, 0x0020802a, 0x00000000,
-        0x00000005, 0x0800000f, 0x00100042, 0x00000001, 0x00101046, 0x00000000, 0x00208046, 0x00000000,
-        0x00000006, 0x08000000, 0x00100022, 0x00000002, 0x0010002a, 0x00000001, 0x0020802a, 0x00000000,
-        0x00000006, 0x09000045, 0x001000f2, 0x00000002, 0x00100046, 0x00000002, 0x00107e46, 0x00000001,
-        0x00106000, 0x00000001, 0x0a000037, 0x00100042, 0x00000001, 0x0020803a, 0x00000000, 0x00000006,
-        0x00004001, 0x3f800000, 0x0010003a, 0x00000002, 0x08000038, 0x00100012, 0x00000001, 0x0010002a,
-        0x00000001, 0x0020801a, 0x00000000, 0x00000004, 0x01000015, 0x0a000037, 0x00100012, 0x00000001,
-        0x0010001a, 0x00000001, 0x0010000a, 0x00000001, 0x0020801a, 0x00000000, 0x00000004, 0x01000015,
-        0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00100006, 0x00000001, 0x01000012,
-        0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x01000015, 0x08000027, 0x00100012,
-        0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x00004001, 0x00000000, 0x0500003b, 0x00100022,
-        0x00000000, 0x0010000a, 0x00000000, 0x0500000b, 0x00100032, 0x00000001, 0x00101046, 0x00000001,
-        0x0500000c, 0x001000c2, 0x00000001, 0x00101406, 0x00000001, 0x07000000, 0x00100042, 0x00000000,
-        0x0010100a, 0x00000001, 0x0010100a, 0x00000001, 0x0a000032, 0x001000c2, 0x00000000, 0x00100aa6,
-        0x00000000, 0x00100806, 0x00000001, 0x80100d56, 0x00000041, 0x00000001, 0x0700000f, 0x00100012,
-        0x00000001, 0x00100ae6, 0x00000000, 0x00101ae6, 0x00000001, 0x07000031, 0x00100012, 0x00000001,
-        0x0010000a, 0x00000001, 0x00004001, 0x00000000, 0x07000001, 0x00100012, 0x00000001, 0x0010000a,
-        0x00000000, 0x0010000a, 0x00000001, 0x0304000d, 0x0010000a, 0x00000001, 0x07000038, 0x00100032,
-        0x00000001, 0x00100ff6, 0x00000000, 0x00101046, 0x00000003, 0x09000032, 0x001000c2, 0x00000000,
-        0x00101406, 0x00000002, 0x00100aa6, 0x00000000, 0x00100406, 0x00000001, 0x0700000f, 0x00100042,
-        0x00000000, 0x00100ae6, 0x00000000, 0x00100ae6, 0x00000000, 0x0500004b, 0x00100042, 0x00000000,
-        0x0010002a, 0x00000000, 0x0a000032, 0x00100082, 0x00000000, 0x0010100a, 0x00000001, 0x0010100a,
-        0x00000001, 0x8010101a, 0x00000041, 0x00000001, 0x08000000, 0x00100042, 0x00000000, 0x8010003a,
-        0x000000c1, 0x00000000, 0x0010002a, 0x00000000, 0x07000031, 0x00100042, 0x00000000, 0x0010002a,
-        0x00000000, 0x00004001, 0x00000000, 0x07000001, 0x00100012, 0x00000000, 0x0010000a, 0x00000000,
-        0x0010002a, 0x00000000, 0x0304000d, 0x0010000a, 0x00000000, 0x07000038, 0x00100012, 0x00000000,
-        0x0010003a, 0x00000000, 0x0010102a, 0x00000001, 0x07000031, 0x00100012, 0x00000000, 0x0010000a,
-        0x00000000, 0x00004001, 0x00000000, 0x07000001, 0x00100012, 0x00000000, 0x0010001a, 0x00000000,
-        0x0010000a, 0x00000000, 0x0304000d, 0x0010000a, 0x00000000, 0x0100003e,
+        0x00000001, 0x0020800a, 0x00000000, 0x00000004, 0x00004001, 0x00000001, 0x0304001f, 0x0010001a,
+        0x00000001, 0x09000000, 0x001000c2, 0x00000001, 0x00101406, 0x00000000, 0x80208406, 0x00000041,
+        0x00000000, 0x00000005, 0x0a000000, 0x00100032, 0x00000002, 0x80208046, 0x00000041, 0x00000000,
+        0x00000005, 0x00208ae6, 0x00000000, 0x00000005, 0x0700000f, 0x00100042, 0x00000001, 0x00100046,
+        0x00000002, 0x00100ae6, 0x00000001, 0x0700000f, 0x00100082, 0x00000001, 0x00100046, 0x00000002,
+        0x00100046, 0x00000002, 0x0700000e, 0x00100042, 0x00000001, 0x0010002a, 0x00000001, 0x0010003a,
+        0x00000001, 0x0a00002d, 0x001000f2, 0x00000002, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
+        0x00000000, 0x00107e46, 0x00000003, 0x0a00002d, 0x001000f2, 0x00000003, 0x00004002, 0x00000001,
+        0x00000001, 0x00000001, 0x00000001, 0x00107e46, 0x00000003, 0x0700001d, 0x00100082, 0x00000001,
+        0x0010002a, 0x00000001, 0x0010000a, 0x00000002, 0x0304001f, 0x0010003a, 0x00000001, 0x05000036,
+        0x00100082, 0x00000001, 0x0010003a, 0x00000003, 0x05000036, 0x00100022, 0x00000002, 0x0010000a,
+        0x00000002, 0x05000036, 0x001000c2, 0x00000002, 0x00100ff6, 0x00000003, 0x08000036, 0x00100032,
+        0x00000003, 0x00004002, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x01000030, 0x08000050,
+        0x00100042, 0x00000003, 0x0010000a, 0x00000003, 0x0020800a, 0x00000000, 0x00000006, 0x05000036,
+        0x00100022, 0x00000003, 0x00004001, 0x00000000, 0x03040003, 0x0010002a, 0x00000003, 0x07000029,
+        0x00100042, 0x00000003, 0x0010000a, 0x00000003, 0x00004001, 0x00000001, 0x0700002d, 0x001000f2,
+        0x00000004, 0x00100aa6, 0x00000003, 0x00107e46, 0x00000003, 0x0700001e, 0x00100042, 0x00000003,
+        0x0010002a, 0x00000003, 0x00004001, 0x00000001, 0x0700002d, 0x001000f2, 0x00000005, 0x00100aa6,
+        0x00000003, 0x00107e46, 0x00000003, 0x0700001d, 0x00100042, 0x00000003, 0x0010002a, 0x00000001,
+        0x0010001a, 0x00000002, 0x0700001d, 0x00100022, 0x00000004, 0x0010000a, 0x00000004, 0x0010002a,
+        0x00000001, 0x07000001, 0x00100042, 0x00000003, 0x0010002a, 0x00000003, 0x0010001a, 0x00000004,
+        0x0304001f, 0x0010002a, 0x00000003, 0x08000000, 0x00100022, 0x00000004, 0x0010002a, 0x00000001,
+        0x8010001a, 0x00000041, 0x00000002, 0x08000000, 0x00100042, 0x00000004, 0x8010001a, 0x00000041,
+        0x00000002, 0x0010000a, 0x00000004, 0x0700000e, 0x00100022, 0x00000004, 0x0010001a, 0x00000004,
+        0x0010002a, 0x00000004, 0x08000000, 0x00100042, 0x00000004, 0x8010002a, 0x00000041, 0x00000002,
+        0x0010003a, 0x00000005, 0x09000032, 0x00100022, 0x00000004, 0x0010001a, 0x00000004, 0x0010002a,
+        0x00000004, 0x0010002a, 0x00000002, 0x05000036, 0x00100082, 0x00000002, 0x0010003a, 0x00000005,
+        0x05000036, 0x00100022, 0x00000003, 0x00004001, 0xffffffff, 0x05000036, 0x00100082, 0x00000001,
+        0x0010001a, 0x00000004, 0x01000002, 0x01000015, 0x05000036, 0x00100022, 0x00000002, 0x0010000a,
+        0x00000004, 0x05000036, 0x00100042, 0x00000002, 0x0010003a, 0x00000005, 0x0700001e, 0x00100012,
+        0x00000003, 0x0010000a, 0x00000003, 0x00004001, 0x00000001, 0x05000036, 0x00100082, 0x00000002,
+        0x0010003a, 0x00000005, 0x05000036, 0x00100032, 0x00000003, 0x00100086, 0x00000003, 0x01000016,
+        0x09000037, 0x00100042, 0x00000001, 0x0010001a, 0x00000003, 0x0010003a, 0x00000001, 0x0010003a,
+        0x00000002, 0x01000012, 0x05000036, 0x00100042, 0x00000001, 0x0010003a, 0x00000003, 0x01000015,
+        0x08000038, 0x00100012, 0x00000001, 0x0010002a, 0x00000001, 0x0020801a, 0x00000000, 0x00000004,
+        0x01000015, 0x0300001f, 0x0010001a, 0x00000001, 0x08000020, 0x00100022, 0x00000001, 0x0020800a,
+        0x00000000, 0x00000004, 0x00004001, 0x00000002, 0x0304001f, 0x0010001a, 0x00000001, 0x0800000f,
+        0x00100042, 0x00000001, 0x00101046, 0x00000000, 0x00208046, 0x00000000, 0x00000005, 0x08000000,
+        0x00100012, 0x00000002, 0x0010002a, 0x00000001, 0x0020802a, 0x00000000, 0x00000005, 0x0800000f,
+        0x00100042, 0x00000001, 0x00101046, 0x00000000, 0x00208046, 0x00000000, 0x00000006, 0x08000000,
+        0x00100022, 0x00000002, 0x0010002a, 0x00000001, 0x0020802a, 0x00000000, 0x00000006, 0x09000045,
+        0x001000f2, 0x00000002, 0x00100046, 0x00000002, 0x00107e46, 0x00000001, 0x00106000, 0x00000001,
+        0x0a000037, 0x00100042, 0x00000001, 0x0020803a, 0x00000000, 0x00000006, 0x00004001, 0x3f800000,
+        0x0010003a, 0x00000002, 0x08000038, 0x00100012, 0x00000001, 0x0010002a, 0x00000001, 0x0020801a,
+        0x00000000, 0x00000004, 0x01000015, 0x0a000037, 0x00100012, 0x00000001, 0x0010001a, 0x00000001,
+        0x0010000a, 0x00000001, 0x0020801a, 0x00000000, 0x00000004, 0x01000015, 0x01000015, 0x07000038,
+        0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00100006, 0x00000001, 0x01000012, 0x05000036,
+        0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x01000015, 0x08000027, 0x00100012, 0x00000000,
+        0x0020800a, 0x00000000, 0x00000000, 0x00004001, 0x00000000, 0x0500003b, 0x00100022, 0x00000000,
+        0x0010000a, 0x00000000, 0x0500000b, 0x00100032, 0x00000001, 0x00101046, 0x00000001, 0x0500000c,
+        0x001000c2, 0x00000001, 0x00101406, 0x00000001, 0x07000000, 0x00100042, 0x00000000, 0x0010100a,
+        0x00000001, 0x0010100a, 0x00000001, 0x0a000032, 0x001000c2, 0x00000000, 0x00100aa6, 0x00000000,
+        0x00100806, 0x00000001, 0x80100d56, 0x00000041, 0x00000001, 0x0700000f, 0x00100012, 0x00000001,
+        0x00100ae6, 0x00000000, 0x00101ae6, 0x00000001, 0x07000031, 0x00100012, 0x00000001, 0x0010000a,
+        0x00000001, 0x00004001, 0x00000000, 0x07000001, 0x00100012, 0x00000001, 0x0010000a, 0x00000000,
+        0x0010000a, 0x00000001, 0x0304000d, 0x0010000a, 0x00000001, 0x07000038, 0x00100032, 0x00000001,
+        0x00100ff6, 0x00000000, 0x00101046, 0x00000003, 0x09000032, 0x001000c2, 0x00000000, 0x00101406,
+        0x00000002, 0x00100aa6, 0x00000000, 0x00100406, 0x00000001, 0x0700000f, 0x00100042, 0x00000000,
+        0x00100ae6, 0x00000000, 0x00100ae6, 0x00000000, 0x0500004b, 0x00100042, 0x00000000, 0x0010002a,
+        0x00000000, 0x0a000032, 0x00100082, 0x00000000, 0x0010100a, 0x00000001, 0x0010100a, 0x00000001,
+        0x8010101a, 0x00000041, 0x00000001, 0x08000000, 0x00100042, 0x00000000, 0x8010003a, 0x000000c1,
+        0x00000000, 0x0010002a, 0x00000000, 0x07000031, 0x00100042, 0x00000000, 0x0010002a, 0x00000000,
+        0x00004001, 0x00000000, 0x07000001, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x0010002a,
+        0x00000000, 0x0304000d, 0x0010000a, 0x00000000, 0x07000038, 0x00100012, 0x00000000, 0x0010003a,
+        0x00000000, 0x0010102a, 0x00000001, 0x07000031, 0x00100012, 0x00000000, 0x0010000a, 0x00000000,
+        0x00004001, 0x00000000, 0x07000001, 0x00100012, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a,
+        0x00000000, 0x0304000d, 0x0010000a, 0x00000000, 0x0100003e,
     };
     static const struct shape_info
     {
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c
index 2cb15e6..6435dad 100644
--- a/dlls/d2d1/tests/d2d1.c
+++ b/dlls/d2d1/tests/d2d1.c
@@ -274,6 +274,27 @@ static void release_resource_readback(struct resource_readback *rb)
     ID3D10Resource_Release(rb->resource);
 }
 
+static DWORD get_readback_colour(struct resource_readback *rb, unsigned int x, unsigned int y)
+{
+    return ((DWORD *)((BYTE *)rb->map_desc.pData + y * rb->map_desc.RowPitch))[x];
+}
+
+static BOOL compare_colour(DWORD c1, DWORD c2, BYTE max_diff)
+{
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff)
+        return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff)
+        return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff)
+        return FALSE;
+    c1 >>= 8; c2 >>= 8;
+    if (abs((c1 & 0xff) - (c2 & 0xff)) > max_diff)
+        return FALSE;
+    return TRUE;
+}
+
 static BOOL compare_float(float f, float g, unsigned int ulps)
 {
     int x = *(int *)&f;
@@ -1674,6 +1695,212 @@ static void test_bitmap_brush(void)
     DestroyWindow(window);
 }
 
+static void test_linear_brush(void)
+{
+    D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES gradient_properties;
+    ID2D1GradientStopCollection *gradient, *tmp_gradient;
+    ID2D1TransformedGeometry *transformed_geometry;
+    ID2D1RectangleGeometry *rectangle_geometry;
+    D2D1_MATRIX_3X2_F matrix, tmp_matrix;
+    ID2D1LinearGradientBrush *brush;
+    struct resource_readback rb;
+    IDXGISwapChain *swapchain;
+    ID2D1RenderTarget *rt;
+    ID3D10Device1 *device;
+    IDXGISurface *surface;
+    ID2D1Factory *factory;
+    D2D1_COLOR_F colour;
+    D2D1_POINT_2F p;
+    unsigned int i;
+    ULONG refcount;
+    D2D1_RECT_F r;
+    float opacity;
+    HWND window;
+    HRESULT hr;
+
+    static const D2D1_GRADIENT_STOP stops[] =
+    {
+        {0.0f, {1.0f, 0.0f, 0.0f, 1.0f}},
+        {0.5f, {0.0f, 1.0f, 0.0f, 1.0f}},
+        {1.0f, {0.0f, 0.0f, 1.0f, 1.0f}},
+    };
+    static const struct
+    {
+        unsigned int x, y;
+        DWORD colour;
+    }
+    test1[] =
+    {
+        {80,  80, 0xff857a00}, {240,  80, 0xff926d00}, {400,  80, 0xff9f6000}, {560,  80, 0xffac5300},
+        {80, 240, 0xff00eb14}, {240, 240, 0xff00f807}, {400, 240, 0xff06f900}, {560, 240, 0xff13ec00},
+        {80, 400, 0xff0053ac}, {240, 400, 0xff005fa0}, {400, 400, 0xff006c93}, {560, 400, 0xff007986},
+    },
+    test2[] =
+    {
+        { 40,  30, 0xff005ba4}, {120,  30, 0xffffffff}, { 40,  60, 0xffffffff}, { 80,  60, 0xff00b44b},
+        {120,  60, 0xff006c93}, {200,  60, 0xffffffff}, { 40,  90, 0xffffffff}, {120,  90, 0xff0ef100},
+        {160,  90, 0xff00c53a}, {200,  90, 0xffffffff}, { 80, 120, 0xffffffff}, {120, 120, 0xffaf5000},
+        {160, 120, 0xff679800}, {200, 120, 0xff1fe000}, {240, 120, 0xffffffff}, {160, 150, 0xffffffff},
+        {200, 150, 0xffc03e00}, {240, 150, 0xffffffff}, {280, 150, 0xffffffff}, {320, 150, 0xffffffff},
+        {240, 180, 0xffffffff}, {280, 180, 0xffff4040}, {320, 180, 0xffff4040}, {380, 180, 0xffffffff},
+        {200, 210, 0xffffffff}, {240, 210, 0xffa99640}, {280, 210, 0xffb28d40}, {320, 210, 0xffbb8440},
+        {360, 210, 0xffc47b40}, {400, 210, 0xffffffff}, {200, 240, 0xffffffff}, {280, 240, 0xff41fd40},
+        {320, 240, 0xff49f540}, {360, 240, 0xff52ec40}, {440, 240, 0xffffffff}, {240, 270, 0xffffffff},
+        {280, 270, 0xff408eb0}, {320, 270, 0xff4097a7}, {360, 270, 0xff40a19e}, {440, 270, 0xffffffff},
+        {280, 300, 0xffffffff}, {320, 300, 0xff4040ff}, {360, 300, 0xff4040ff}, {400, 300, 0xff406ad4},
+        {440, 300, 0xff4061de}, {480, 300, 0xff4057e7}, {520, 300, 0xff404ef1}, {280, 330, 0xffffffff},
+        {360, 330, 0xffffffff}, {400, 330, 0xff40c17e}, {440, 330, 0xff40b788}, {480, 330, 0xff40ae91},
+        {520, 330, 0xff40a49b}, {400, 360, 0xff57e740}, {440, 360, 0xff4ef140}, {480, 360, 0xff44fb40},
+        {520, 360, 0xff40fa45}, {400, 390, 0xffae9140}, {440, 390, 0xffa49b40}, {480, 390, 0xff9aa540},
+        {520, 390, 0xff90ae40},
+    };
+
+    if (!(device = create_device()))
+    {
+        skip("Failed to create device, skipping tests.\n");
+        return;
+    }
+    window = create_window();
+    swapchain = create_swapchain(device, window, TRUE);
+    hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_IDXGISurface, (void **)&surface);
+    ok(SUCCEEDED(hr), "Failed to get buffer, hr %#x.\n", hr);
+    rt = create_render_target(surface);
+    ok(!!rt, "Failed to create render target.\n");
+
+    ID2D1RenderTarget_SetDpi(rt, 192.0f, 48.0f);
+    ID2D1RenderTarget_SetAntialiasMode(rt, D2D1_ANTIALIAS_MODE_ALIASED);
+
+    hr = ID2D1RenderTarget_CreateGradientStopCollection(rt, stops, sizeof(stops) / sizeof(*stops),
+            D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, &gradient);
+    ok(SUCCEEDED(hr), "Failed to create stop collection, hr %#x.\n", hr);
+
+    set_point(&gradient_properties.startPoint, 320.0f, 0.0f);
+    set_point(&gradient_properties.endPoint, 0.0f, 960.0f);
+    hr = ID2D1RenderTarget_CreateLinearGradientBrush(rt, &gradient_properties, NULL, gradient, &brush);
+    ok(SUCCEEDED(hr), "Failed to create brush, hr %#x.\n", hr);
+
+    opacity = ID2D1LinearGradientBrush_GetOpacity(brush);
+    ok(opacity == 1.0f, "Got unexpected opacity %.8e.\n", opacity);
+    set_matrix_identity(&matrix);
+    ID2D1LinearGradientBrush_GetTransform(brush, &tmp_matrix);
+    ok(!memcmp(&tmp_matrix, &matrix, sizeof(matrix)),
+            "Got unexpected matrix {%.8e, %.8e, %.8e, %.8e, %.8e, %.8e}.\n",
+            tmp_matrix._11, tmp_matrix._12, tmp_matrix._21,
+            tmp_matrix._22, tmp_matrix._31, tmp_matrix._32);
+    p = ID2D1LinearGradientBrush_GetStartPoint(brush);
+    ok(compare_point(&p, 320.0f, 0.0f, 0), "Got unexpected start point {%.8e, %.8e}.\n", p.x, p.y);
+    p = ID2D1LinearGradientBrush_GetEndPoint(brush);
+    ok(compare_point(&p, 0.0f, 960.0f, 0), "Got unexpected end point {%.8e, %.8e}.\n", p.x, p.y);
+    ID2D1LinearGradientBrush_GetGradientStopCollection(brush, &tmp_gradient);
+    ok(tmp_gradient == gradient, "Got unexpected gradient %p, expected %p.\n", tmp_gradient, gradient);
+    ID2D1GradientStopCollection_Release(tmp_gradient);
+
+    ID2D1RenderTarget_BeginDraw(rt);
+
+    set_color(&colour, 1.0f, 1.0f, 1.0f, 1.0f);
+    ID2D1RenderTarget_Clear(rt, &colour);
+
+    set_rect(&r, 0.0f, 0.0f, 320.0f, 960.0f);
+    ID2D1RenderTarget_FillRectangle(rt, &r, (ID2D1Brush *)brush);
+
+    hr = ID2D1RenderTarget_EndDraw(rt, NULL, NULL);
+    ok(SUCCEEDED(hr), "Failed to end draw, hr %#x.\n", hr);
+
+    get_surface_readback(surface, &rb);
+    for (i = 0; i < sizeof(test1) / sizeof(*test1); ++i)
+    {
+        DWORD colour;
+
+        colour = get_readback_colour(&rb, test1[i].x, test1[i].y);
+        ok(compare_colour(colour, test1[i].colour, 1),
+                "Got unexpected colour 0x%08x at position {%u, %u}.\n",
+                colour, test1[i].x, test1[i].y);
+    }
+    release_resource_readback(&rb);
+
+    ID2D1RenderTarget_BeginDraw(rt);
+
+    ID2D1RenderTarget_Clear(rt, &colour);
+
+    set_matrix_identity(&matrix);
+    skew_matrix(&matrix, 0.2146f, 1.575f);
+    ID2D1RenderTarget_SetTransform(rt, &matrix);
+
+    set_matrix_identity(&matrix);
+    translate_matrix(&matrix, 0.0f, 240.0f);
+    scale_matrix(&matrix, 0.25f, -0.25f);
+    ID2D1LinearGradientBrush_SetTransform(brush, &matrix);
+
+    set_rect(&r, 0.0f, 0.0f, 80.0f, 240.0f);
+    ID2D1RenderTarget_FillRectangle(rt, &r, (ID2D1Brush *)brush);
+
+    set_matrix_identity(&matrix);
+    scale_matrix(&matrix, 0.5f, 2.0f);
+    translate_matrix(&matrix, 320.0f, 240.0f);
+    rotate_matrix(&matrix, M_PI / 4.0f);
+    ID2D1RenderTarget_SetTransform(rt, &matrix);
+
+    set_matrix_identity(&matrix);
+    translate_matrix(&matrix, 0.0f, -50.0f);
+    scale_matrix(&matrix, 0.1f, 0.1f);
+    rotate_matrix(&matrix, -M_PI / 3.0f);
+    ID2D1LinearGradientBrush_SetTransform(brush, &matrix);
+
+    ID2D1LinearGradientBrush_SetOpacity(brush, 0.75f);
+    set_rect(&r, -80.0f, -60.0f, 80.0f, 60.0f);
+    ID2D1RenderTarget_FillRectangle(rt, &r, (ID2D1Brush *)brush);
+
+    ID2D1RenderTarget_GetFactory(rt, &factory);
+
+    set_rect(&r, -1.0f, -1.0f, 1.0f, 1.0f);
+    hr = ID2D1Factory_CreateRectangleGeometry(factory, &r, &rectangle_geometry);
+    ok(SUCCEEDED(hr), "Failed to create geometry, hr %#x.\n", hr);
+
+    set_matrix_identity(&matrix);
+    translate_matrix(&matrix, 228.5f, 714.0f);
+    scale_matrix(&matrix, 40.0f, 120.0f);
+    hr = ID2D1Factory_CreateTransformedGeometry(factory, (ID2D1Geometry *)rectangle_geometry,
+            &matrix, &transformed_geometry);
+    ok(SUCCEEDED(hr), "Failed to create geometry, hr %#x.\n", hr);
+    ID2D1RectangleGeometry_Release(rectangle_geometry);
+
+    set_matrix_identity(&matrix);
+    ID2D1RenderTarget_SetTransform(rt, &matrix);
+    ID2D1LinearGradientBrush_SetTransform(brush, &matrix);
+    set_point(&p, 188.5f, 834.0f);
+    ID2D1LinearGradientBrush_SetStartPoint(brush, p);
+    set_point(&p, 268.5f, 594.0f);
+    ID2D1LinearGradientBrush_SetEndPoint(brush, p);
+    ID2D1RenderTarget_FillGeometry(rt, (ID2D1Geometry *)transformed_geometry, (ID2D1Brush *)brush, NULL);
+    ID2D1TransformedGeometry_Release(transformed_geometry);
+
+    ID2D1Factory_Release(factory);
+
+    hr = ID2D1RenderTarget_EndDraw(rt, NULL, NULL);
+    ok(SUCCEEDED(hr), "Failed to end draw, hr %#x.\n", hr);
+
+    get_surface_readback(surface, &rb);
+    for (i = 0; i < sizeof(test2) / sizeof(*test2); ++i)
+    {
+        DWORD colour;
+
+        colour = get_readback_colour(&rb, test2[i].x, test2[i].y);
+        ok(compare_colour(colour, test2[i].colour, 1),
+                "Got unexpected colour 0x%08x at position {%u, %u}.\n",
+                colour, test2[i].x, test2[i].y);
+    }
+    release_resource_readback(&rb);
+
+    ID2D1LinearGradientBrush_Release(brush);
+    refcount = ID2D1GradientStopCollection_Release(gradient);
+    ok(!refcount, "Gradient has %u references left.\n", refcount);
+    ID2D1RenderTarget_Release(rt);
+    IDXGISurface_Release(surface);
+    IDXGISwapChain_Release(swapchain);
+    ID3D10Device1_Release(device);
+    DestroyWindow(window);
+}
+
 static void fill_geometry_sink(ID2D1GeometrySink *sink)
 {
     D2D1_POINT_2F point;
@@ -5949,6 +6176,7 @@ START_TEST(d2d1)
     test_state_block();
     test_color_brush();
     test_bitmap_brush();
+    test_linear_brush();
     test_path_geometry();
     test_rectangle_geometry();
     test_rounded_rectangle_geometry();
-- 
2.1.4




More information about the wine-patches mailing list