Commit b33786ce authored by Henri Verbeet's avatar Henri Verbeet Committed by Alexandre Julliard

d2d1: Implement initial support for drawing bezier curves.

parent 025c3201
......@@ -796,20 +796,21 @@ HRESULT d2d_brush_get_ps_cb(struct d2d_brush *brush, struct d2d_d3d_render_targe
return hr;
}
void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_d3d_render_target *render_target)
void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_d3d_render_target *render_target,
enum d2d_shape_type shape_type)
{
static const float blend_factor[] = {1.0f, 1.0f, 1.0f, 1.0f};
ID3D10Device *device = render_target->device;
ID3D10PixelShader *ps;
HRESULT hr;
ID3D10Device_OMSetBlendState(device, render_target->bs, blend_factor, D3D10_DEFAULT_SAMPLE_MASK);
if (brush->type == D2D_BRUSH_TYPE_SOLID)
{
ID3D10Device_PSSetShader(device, render_target->rect_solid_ps);
}
else if (brush->type == D2D_BRUSH_TYPE_BITMAP)
if (!(ps = render_target->shape_resources[shape_type].ps[brush->type]))
FIXME("No pixel shader for shape type %#x and brush type %#x.\n", shape_type, brush->type);
ID3D10Device_PSSetShader(device, ps);
if (brush->type == D2D_BRUSH_TYPE_BITMAP)
{
ID3D10Device_PSSetShader(device, render_target->rect_bitmap_ps);
ID3D10Device_PSSetShaderResources(device, 0, 1, &brush->u.bitmap.bitmap->view);
if (!brush->u.bitmap.sampler_state)
{
......@@ -838,7 +839,7 @@ void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_d3d_render_tar
}
ID3D10Device_PSSetSamplers(device, 0, 1, &brush->u.bitmap.sampler_state);
}
else
else if (brush->type != D2D_BRUSH_TYPE_SOLID)
{
FIXME("Unhandled brush type %#x.\n", brush->type);
}
......
......@@ -35,6 +35,14 @@ enum d2d_brush_type
D2D_BRUSH_TYPE_SOLID,
D2D_BRUSH_TYPE_LINEAR,
D2D_BRUSH_TYPE_BITMAP,
D2D_BRUSH_TYPE_COUNT,
};
enum d2d_shape_type
{
D2D_SHAPE_TYPE_TRIANGLE,
D2D_SHAPE_TYPE_BEZIER,
D2D_SHAPE_TYPE_COUNT,
};
struct d2d_clip_stack
......@@ -44,6 +52,13 @@ struct d2d_clip_stack
unsigned int count;
};
struct d2d_shape_resources
{
ID3D10InputLayout *il;
ID3D10VertexShader *vs;
ID3D10PixelShader *ps[D2D_BRUSH_TYPE_COUNT];
};
struct d2d_d3d_render_target
{
ID2D1RenderTarget ID2D1RenderTarget_iface;
......@@ -54,17 +69,13 @@ struct d2d_d3d_render_target
ID3D10Device *device;
ID3D10RenderTargetView *view;
ID3D10StateBlock *stateblock;
ID3D10InputLayout *il;
struct d2d_shape_resources shape_resources[D2D_SHAPE_TYPE_COUNT];
ID3D10Buffer *ib;
unsigned int vb_stride;
ID3D10Buffer *vb;
ID3D10VertexShader *vs;
ID3D10RasterizerState *rs;
ID3D10BlendState *bs;
ID3D10PixelShader *rect_solid_ps;
ID3D10PixelShader *rect_bitmap_ps;
D2D1_DRAWING_STATE_DESCRIPTION drawing_state;
IDWriteRenderingParams *text_rendering_params;
......@@ -142,7 +153,8 @@ void d2d_linear_gradient_brush_init(struct d2d_brush *brush, ID2D1RenderTarget *
HRESULT d2d_bitmap_brush_init(struct d2d_brush *brush, struct d2d_d3d_render_target *render_target,
ID2D1Bitmap *bitmap, const D2D1_BITMAP_BRUSH_PROPERTIES *bitmap_brush_desc,
const D2D1_BRUSH_PROPERTIES *brush_desc) DECLSPEC_HIDDEN;
void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_d3d_render_target *render_target) DECLSPEC_HIDDEN;
void d2d_brush_bind_resources(struct d2d_brush *brush, struct d2d_d3d_render_target *render_target,
enum d2d_shape_type shape_type) DECLSPEC_HIDDEN;
HRESULT d2d_brush_get_ps_cb(struct d2d_brush *brush, struct d2d_d3d_render_target *render_target,
ID3D10Buffer **ps_cb) DECLSPEC_HIDDEN;
struct d2d_brush *unsafe_impl_from_ID2D1Brush(ID2D1Brush *iface) DECLSPEC_HIDDEN;
......@@ -201,6 +213,18 @@ enum d2d_geometry_state
D2D_GEOMETRY_STATE_FIGURE,
};
struct d2d_bezier
{
struct
{
D2D1_POINT_2F position;
struct
{
float u, v, sign;
} texcoord;
} v[3];
};
struct d2d_face
{
UINT16 v[3];
......@@ -218,6 +242,9 @@ struct d2d_geometry
size_t faces_size;
size_t face_count;
struct d2d_bezier *beziers;
size_t bezier_count;
union
{
struct
......
......@@ -39,6 +39,10 @@ struct d2d_figure
D2D1_POINT_2F *vertices;
size_t vertices_size;
size_t vertex_count;
struct d2d_bezier *beziers;
size_t beziers_size;
size_t bezier_count;
};
struct d2d_cdt_edge_ref
......@@ -71,6 +75,16 @@ static void d2d_point_subtract(D2D1_POINT_2F *out,
out->y = a->y - b->y;
}
static float d2d_point_ccw(const D2D1_POINT_2F *a, const D2D1_POINT_2F *b, const D2D1_POINT_2F *c)
{
D2D1_POINT_2F ab, ac;
d2d_point_subtract(&ab, b, a);
d2d_point_subtract(&ac, c, a);
return ab.x * ac.y - ab.y * ac.x;
}
static BOOL d2d_array_reserve(void **elements, size_t *capacity, size_t element_count, size_t element_size)
{
size_t new_capacity, max_capacity;
......@@ -133,6 +147,55 @@ static BOOL d2d_figure_add_vertex(struct d2d_figure *figure, D2D1_POINT_2F verte
return TRUE;
}
/* FIXME: No inside/outside testing is done for beziers. */
static BOOL d2d_figure_add_bezier(struct d2d_figure *figure, D2D1_POINT_2F p0, D2D1_POINT_2F p1, D2D1_POINT_2F p2)
{
struct d2d_bezier *b;
unsigned int idx1, idx2;
float sign;
if (!d2d_array_reserve((void **)&figure->beziers, &figure->beziers_size,
figure->bezier_count + 1, sizeof(*figure->beziers)))
{
ERR("Failed to grow beziers array.\n");
return FALSE;
}
if (d2d_point_ccw(&p0, &p1, &p2) > 0.0f)
{
sign = -1.0f;
idx1 = 1;
idx2 = 2;
}
else
{
sign = 1.0f;
idx1 = 2;
idx2 = 1;
}
b = &figure->beziers[figure->bezier_count];
b->v[0].position = p0;
b->v[0].texcoord.u = 0.0f;
b->v[0].texcoord.v = 0.0f;
b->v[0].texcoord.sign = sign;
b->v[idx1].position = p1;
b->v[idx1].texcoord.u = 0.5f;
b->v[idx1].texcoord.v = 0.0f;
b->v[idx1].texcoord.sign = sign;
b->v[idx2].position = p2;
b->v[idx2].texcoord.u = 1.0f;
b->v[idx2].texcoord.v = 1.0f;
b->v[idx2].texcoord.sign = sign;
++figure->bezier_count;
if (sign > 0.0f && !d2d_figure_add_vertex(figure, p1))
return FALSE;
if (!d2d_figure_add_vertex(figure, p2))
return FALSE;
return TRUE;
}
static void d2d_cdt_edge_rot(struct d2d_cdt_edge_ref *dst, const struct d2d_cdt_edge_ref *src)
{
dst->idx = src->idx;
......@@ -193,12 +256,7 @@ static void d2d_cdt_edge_set_destination(const struct d2d_cdt *cdt,
static float d2d_cdt_ccw(const struct d2d_cdt *cdt, size_t a, size_t b, size_t c)
{
D2D1_POINT_2F ab, ac;
d2d_point_subtract(&ab, &cdt->vertices[b], &cdt->vertices[a]);
d2d_point_subtract(&ac, &cdt->vertices[c], &cdt->vertices[a]);
return ab.x * ac.y - ab.y * ac.x;
return d2d_point_ccw(&cdt->vertices[a], &cdt->vertices[b], &cdt->vertices[c]);
}
static BOOL d2d_cdt_rightof(const struct d2d_cdt *cdt, size_t p, const struct d2d_cdt_edge_ref *e)
......@@ -773,6 +831,7 @@ static BOOL d2d_cdt_insert_segments(struct d2d_cdt *cdt, struct d2d_geometry *ge
/* Intersect the geometry's segments with themselves. This uses the
* straightforward approach of testing everything against everything, but
* there certainly exist more scalable algorithms for this. */
/* FIXME: Beziers can't currently self-intersect. */
static BOOL d2d_geometry_intersect_self(struct d2d_geometry *geometry)
{
D2D1_POINT_2F p0, p1, q0, q1, v_p, v_q, v_qp, intersection;
......@@ -920,6 +979,7 @@ static BOOL d2d_path_geometry_add_figure(struct d2d_geometry *geometry)
static void d2d_geometry_destroy(struct d2d_geometry *geometry)
{
HeapFree(GetProcessHeap(), 0, geometry->beziers);
HeapFree(GetProcessHeap(), 0, geometry->faces);
HeapFree(GetProcessHeap(), 0, geometry->vertices);
HeapFree(GetProcessHeap(), 0, geometry);
......@@ -1048,9 +1108,11 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_AddBeziers(ID2D1GeometrySink *if
const D2D1_BEZIER_SEGMENT *beziers, UINT32 count)
{
struct d2d_geometry *geometry = impl_from_ID2D1GeometrySink(iface);
struct d2d_figure *figure = &geometry->u.path.figures[geometry->u.path.figure_count - 1];
D2D1_POINT_2F p;
unsigned int i;
FIXME("iface %p, beziers %p, count %u stub!\n", iface, beziers, count);
TRACE("iface %p, beziers %p, count %u.\n", iface, beziers, count);
if (geometry->u.path.state != D2D_GEOMETRY_STATE_FIGURE)
{
......@@ -1060,9 +1122,14 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_AddBeziers(ID2D1GeometrySink *if
for (i = 0; i < count; ++i)
{
if (!d2d_figure_add_vertex(&geometry->u.path.figures[geometry->u.path.figure_count - 1], beziers[i].point3))
/* FIXME: This tries to approximate a cubic bezier with a quadratic one. */
p.x = (beziers[i].point1.x + beziers[i].point2.x) * 0.75f;
p.y = (beziers[i].point1.y + beziers[i].point2.y) * 0.75f;
p.x -= (figure->vertices[figure->vertex_count - 1].x + beziers[i].point3.x) * 0.25f;
p.y -= (figure->vertices[figure->vertex_count - 1].y + beziers[i].point3.y) * 0.25f;
if (!d2d_figure_add_bezier(figure, figure->vertices[figure->vertex_count - 1], p, beziers[i].point3))
{
ERR("Failed to add vertex.\n");
ERR("Failed to add bezier.\n");
return;
}
}
......@@ -1097,6 +1164,7 @@ static void d2d_path_geometry_free_figures(struct d2d_geometry *geometry)
for (i = 0; i < geometry->u.path.figure_count; ++i)
{
HeapFree(GetProcessHeap(), 0, geometry->u.path.figures[i].beziers);
HeapFree(GetProcessHeap(), 0, geometry->u.path.figures[i].vertices);
}
HeapFree(GetProcessHeap(), 0, geometry->u.path.figures);
......@@ -1108,6 +1176,7 @@ static HRESULT STDMETHODCALLTYPE d2d_geometry_sink_Close(ID2D1GeometrySink *ifac
{
struct d2d_geometry *geometry = impl_from_ID2D1GeometrySink(iface);
HRESULT hr = E_FAIL;
size_t i, start;
TRACE("iface %p.\n", iface);
......@@ -1121,7 +1190,33 @@ static HRESULT STDMETHODCALLTYPE d2d_geometry_sink_Close(ID2D1GeometrySink *ifac
if (!d2d_geometry_intersect_self(geometry))
goto done;
hr = d2d_path_geometry_triangulate(geometry);
if (FAILED(hr = d2d_path_geometry_triangulate(geometry)))
goto done;
for (i = 0; i < geometry->u.path.figure_count; ++i)
{
geometry->bezier_count += geometry->u.path.figures[i].bezier_count;
}
if (!(geometry->beziers = HeapAlloc(GetProcessHeap(), 0,
geometry->bezier_count * sizeof(*geometry->beziers))))
{
ERR("Failed to allocate beziers array.\n");
geometry->bezier_count = 0;
hr = E_OUTOFMEMORY;
goto done;
}
for (i = 0, start = 0; i < geometry->u.path.figure_count; ++i)
{
struct d2d_figure *figure = &geometry->u.path.figures[i];
if (figure->bezier_count)
{
memcpy(&geometry->beziers[start], figure->beziers,
figure->bezier_count * sizeof(*figure->beziers));
start += figure->bezier_count;
}
}
done:
d2d_path_geometry_free_figures(geometry);
......@@ -1156,9 +1251,10 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_AddQuadraticBeziers(ID2D1Geometr
const D2D1_QUADRATIC_BEZIER_SEGMENT *beziers, UINT32 bezier_count)
{
struct d2d_geometry *geometry = impl_from_ID2D1GeometrySink(iface);
struct d2d_figure *figure = &geometry->u.path.figures[geometry->u.path.figure_count - 1];
unsigned int i;
FIXME("iface %p, beziers %p, bezier_count %u stub!\n", iface, beziers, bezier_count);
TRACE("iface %p, beziers %p, bezier_count %u.\n", iface, beziers, bezier_count);
if (geometry->u.path.state != D2D_GEOMETRY_STATE_FIGURE)
{
......@@ -1168,9 +1264,10 @@ static void STDMETHODCALLTYPE d2d_geometry_sink_AddQuadraticBeziers(ID2D1Geometr
for (i = 0; i < bezier_count; ++i)
{
if (!d2d_figure_add_vertex(&geometry->u.path.figures[geometry->u.path.figure_count - 1], beziers[i].point2))
if (!d2d_figure_add_bezier(figure, figure->vertices[figure->vertex_count - 1],
beziers[i].point1, beziers[i].point2))
{
ERR("Failed to add vertex.\n");
ERR("Failed to add bezier.\n");
return;
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment