From: Dmitry Timoshkov dmitry@baikal.ru
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54368 Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/d2d1/d2d1_private.h | 1 + dlls/d2d1/device.c | 8 +- dlls/d2d1/device.c.orig | 4708 ++++++++++++++++++++++++++++++++++++++ dlls/d2d1/stroke.c | 10 +- 4 files changed, 4724 insertions(+), 3 deletions(-) create mode 100644 dlls/d2d1/device.c.orig
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index f3029bab0f2..1dd5cdb0a39 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -383,6 +383,7 @@ struct d2d_stroke_style
HRESULT d2d_stroke_style_init(struct d2d_stroke_style *style, ID2D1Factory *factory, const D2D1_STROKE_STYLE_PROPERTIES1 *desc, const float *dashes, UINT32 dash_count); +struct d2d_stroke_style *unsafe_impl_from_ID2D1StrokeStyle(ID2D1StrokeStyle *iface);
struct d2d_layer { diff --git a/dlls/d2d1/device.c b/dlls/d2d1/device.c index 33cf943010c..aa8902c35e9 100644 --- a/dlls/d2d1/device.c +++ b/dlls/d2d1/device.c @@ -942,6 +942,7 @@ static void STDMETHODCALLTYPE d2d_device_context_DrawGeometry(ID2D1DeviceContext const struct d2d_geometry *geometry_impl = unsafe_impl_from_ID2D1Geometry(geometry); struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); struct d2d_brush *brush_impl = unsafe_impl_from_ID2D1Brush(brush); + struct d2d_stroke_style *stroke_style_impl = unsafe_impl_from_ID2D1StrokeStyle(stroke_style);
TRACE("iface %p, geometry %p, brush %p, stroke_width %.8e, stroke_style %p.\n", iface, geometry, brush, stroke_width, stroke_style); @@ -953,8 +954,11 @@ static void STDMETHODCALLTYPE d2d_device_context_DrawGeometry(ID2D1DeviceContext return; }
- if (stroke_style) - FIXME("Ignoring stroke style %p.\n", stroke_style); + if (stroke_style_impl) + { + if (stroke_style_impl->desc.transformType == D2D1_STROKE_TRANSFORM_TYPE_FIXED) + stroke_width /= context->drawing_state.transform.m11; + }
d2d_device_context_draw_geometry(context, geometry_impl, brush_impl, stroke_width); } diff --git a/dlls/d2d1/device.c.orig b/dlls/d2d1/device.c.orig new file mode 100644 index 00000000000..33cf943010c --- /dev/null +++ b/dlls/d2d1/device.c.orig @@ -0,0 +1,4708 @@ +/* + * Copyright 2014 Henri Verbeet for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "d2d1_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d2d); + +#define INITIAL_CLIP_STACK_SIZE 4 + +static const D2D1_MATRIX_3X2_F identity = +{{{ + 1.0f, 0.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, +}}}; + +struct d2d_draw_text_layout_ctx +{ + ID2D1Brush *brush; + D2D1_DRAW_TEXT_OPTIONS options; +}; + +static inline struct d2d_device *impl_from_ID2D1Device(ID2D1Device1 *iface) +{ + return CONTAINING_RECORD(iface, struct d2d_device, ID2D1Device1_iface); +} + +static ID2D1Brush *d2d_draw_get_text_brush(struct d2d_draw_text_layout_ctx *context, IUnknown *effect) +{ + ID2D1Brush *brush = NULL; + + if (effect && SUCCEEDED(IUnknown_QueryInterface(effect, &IID_ID2D1Brush, (void**)&brush))) + return brush; + + ID2D1Brush_AddRef(context->brush); + return context->brush; +} + +static void d2d_rect_intersect(D2D1_RECT_F *dst, const D2D1_RECT_F *src) +{ + if (src->left > dst->left) + dst->left = src->left; + if (src->top > dst->top) + dst->top = src->top; + if (src->right < dst->right) + dst->right = src->right; + if (src->bottom < dst->bottom) + dst->bottom = src->bottom; +} + +static void d2d_rect_set(D2D1_RECT_F *dst, float left, float top, float right, float bottom) +{ + dst->left = left; + dst->top = top; + dst->right = right; + dst->bottom = bottom; +} + +static void d2d_size_set(D2D1_SIZE_U *dst, float width, float height) +{ + dst->width = width; + dst->height = height; +} + +static BOOL d2d_clip_stack_init(struct d2d_clip_stack *stack) +{ + if (!(stack->stack = malloc(INITIAL_CLIP_STACK_SIZE * sizeof(*stack->stack)))) + return FALSE; + + stack->size = INITIAL_CLIP_STACK_SIZE; + stack->count = 0; + + return TRUE; +} + +static void d2d_clip_stack_cleanup(struct d2d_clip_stack *stack) +{ + free(stack->stack); +} + +static BOOL d2d_clip_stack_push(struct d2d_clip_stack *stack, const D2D1_RECT_F *rect) +{ + D2D1_RECT_F r; + + if (!d2d_array_reserve((void **)&stack->stack, &stack->size, stack->count + 1, sizeof(*stack->stack))) + return FALSE; + + r = *rect; + if (stack->count) + d2d_rect_intersect(&r, &stack->stack[stack->count - 1]); + stack->stack[stack->count++] = r; + + return TRUE; +} + +static void d2d_clip_stack_pop(struct d2d_clip_stack *stack) +{ + if (!stack->count) + return; + --stack->count; +} + +static void d2d_device_context_draw(struct d2d_device_context *render_target, enum d2d_shape_type shape_type, + ID3D11Buffer *ib, unsigned int index_count, ID3D11Buffer *vb, unsigned int vb_stride, + struct d2d_brush *brush, struct d2d_brush *opacity_brush) +{ + struct d2d_shape_resources *shape_resources = &render_target->shape_resources[shape_type]; + ID3DDeviceContextState *prev_state; + ID3D11Device1 *device = render_target->d3d_device; + ID3D11DeviceContext1 *context; + ID3D11Buffer *vs_cb = render_target->vs_cb, *ps_cb = render_target->ps_cb; + D3D11_RECT scissor_rect; + unsigned int offset; + D3D11_VIEWPORT vp; + + vp.TopLeftX = 0; + vp.TopLeftY = 0; + vp.Width = render_target->pixel_size.width; + vp.Height = render_target->pixel_size.height; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + + ID3D11Device1_GetImmediateContext1(device, &context); + ID3D11DeviceContext1_SwapDeviceContextState(context, render_target->d3d_state, &prev_state); + + ID3D11DeviceContext1_IASetInputLayout(context, shape_resources->il); + ID3D11DeviceContext1_IASetPrimitiveTopology(context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + ID3D11DeviceContext1_IASetIndexBuffer(context, ib, DXGI_FORMAT_R16_UINT, 0); + offset = 0; + ID3D11DeviceContext1_IASetVertexBuffers(context, 0, 1, &vb, &vb_stride, &offset); + ID3D11DeviceContext1_VSSetConstantBuffers(context, 0, 1, &vs_cb); + ID3D11DeviceContext1_VSSetShader(context, shape_resources->vs, NULL, 0); + ID3D11DeviceContext1_PSSetConstantBuffers(context, 0, 1, &ps_cb); + ID3D11DeviceContext1_PSSetShader(context, render_target->ps, NULL, 0); + ID3D11DeviceContext1_RSSetViewports(context, 1, &vp); + if (render_target->clip_stack.count) + { + const D2D1_RECT_F *clip_rect; + + clip_rect = &render_target->clip_stack.stack[render_target->clip_stack.count - 1]; + scissor_rect.left = ceilf(clip_rect->left - 0.5f); + scissor_rect.top = ceilf(clip_rect->top - 0.5f); + scissor_rect.right = ceilf(clip_rect->right - 0.5f); + scissor_rect.bottom = ceilf(clip_rect->bottom - 0.5f); + } + else + { + scissor_rect.left = 0.0f; + scissor_rect.top = 0.0f; + scissor_rect.right = render_target->pixel_size.width; + scissor_rect.bottom = render_target->pixel_size.height; + } + ID3D11DeviceContext1_RSSetScissorRects(context, 1, &scissor_rect); + ID3D11DeviceContext1_RSSetState(context, render_target->rs); + ID3D11DeviceContext1_OMSetRenderTargets(context, 1, &render_target->target.bitmap->rtv, NULL); + if (brush) + { + ID3D11DeviceContext1_OMSetBlendState(context, render_target->bs, NULL, D3D11_DEFAULT_SAMPLE_MASK); + d2d_brush_bind_resources(brush, render_target, 0); + } + else + { + ID3D11DeviceContext1_OMSetBlendState(context, NULL, NULL, D3D11_DEFAULT_SAMPLE_MASK); + } + if (opacity_brush) + d2d_brush_bind_resources(opacity_brush, render_target, 1); + + if (ib) + ID3D11DeviceContext1_DrawIndexed(context, index_count, 0, 0); + else + ID3D11DeviceContext1_Draw(context, index_count, 0); + + ID3D11DeviceContext1_SwapDeviceContextState(context, prev_state, NULL); + ID3D11DeviceContext1_Release(context); + ID3DDeviceContextState_Release(prev_state); +} + +static void d2d_device_context_set_error(struct d2d_device_context *context, HRESULT code) +{ + context->error.code = code; + context->error.tag1 = context->drawing_state.tag1; + context->error.tag2 = context->drawing_state.tag2; +} + +static inline struct d2d_device_context *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct d2d_device_context, IUnknown_iface); +} + +static inline struct d2d_device_context *impl_from_ID2D1DeviceContext(ID2D1DeviceContext1 *iface) +{ + return CONTAINING_RECORD(iface, struct d2d_device_context, ID2D1DeviceContext1_iface); +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_inner_QueryInterface(IUnknown *iface, REFIID iid, void **out) +{ + struct d2d_device_context *context = impl_from_IUnknown(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_ID2D1DeviceContext1) + || IsEqualGUID(iid, &IID_ID2D1DeviceContext) + || IsEqualGUID(iid, &IID_ID2D1RenderTarget) + || IsEqualGUID(iid, &IID_ID2D1Resource) + || IsEqualGUID(iid, &IID_IUnknown)) + { + ID2D1DeviceContext1_AddRef(&context->ID2D1DeviceContext1_iface); + *out = &context->ID2D1DeviceContext1_iface; + return S_OK; + } + else if (IsEqualGUID(iid, &IID_ID2D1GdiInteropRenderTarget)) + { + ID2D1GdiInteropRenderTarget_AddRef(&context->ID2D1GdiInteropRenderTarget_iface); + *out = &context->ID2D1GdiInteropRenderTarget_iface; + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG STDMETHODCALLTYPE d2d_device_context_inner_AddRef(IUnknown *iface) +{ + struct d2d_device_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedIncrement(&context->refcount); + + TRACE("%p increasing refcount to %lu.\n", iface, refcount); + + return refcount; +} + +static ULONG STDMETHODCALLTYPE d2d_device_context_inner_Release(IUnknown *iface) +{ + struct d2d_device_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&context->refcount); + + TRACE("%p decreasing refcount to %lu.\n", iface, refcount); + + if (!refcount) + { + unsigned int i, j, k; + + d2d_clip_stack_cleanup(&context->clip_stack); + IDWriteRenderingParams_Release(context->default_text_rendering_params); + if (context->text_rendering_params) + IDWriteRenderingParams_Release(context->text_rendering_params); + if (context->bs) + ID3D11BlendState_Release(context->bs); + ID3D11RasterizerState_Release(context->rs); + ID3D11Buffer_Release(context->vb); + ID3D11Buffer_Release(context->ib); + ID3D11Buffer_Release(context->ps_cb); + ID3D11PixelShader_Release(context->ps); + ID3D11Buffer_Release(context->vs_cb); + for (i = 0; i < D2D_SHAPE_TYPE_COUNT; ++i) + { + ID3D11VertexShader_Release(context->shape_resources[i].vs); + ID3D11InputLayout_Release(context->shape_resources[i].il); + } + for (i = 0; i < D2D_SAMPLER_INTERPOLATION_MODE_COUNT; ++i) + { + for (j = 0; j < D2D_SAMPLER_EXTEND_MODE_COUNT; ++j) + { + for (k = 0; k < D2D_SAMPLER_EXTEND_MODE_COUNT; ++k) + { + if (context->sampler_states[i][j][k]) + ID3D11SamplerState_Release(context->sampler_states[i][j][k]); + } + } + } + if (context->d3d_state) + ID3DDeviceContextState_Release(context->d3d_state); + if (context->target.object) + IUnknown_Release(context->target.object); + ID3D11Device1_Release(context->d3d_device); + ID2D1Factory_Release(context->factory); + ID2D1Device1_Release(&context->device->ID2D1Device1_iface); + free(context); + } + + return refcount; +} + +static const struct IUnknownVtbl d2d_device_context_inner_unknown_vtbl = +{ + d2d_device_context_inner_QueryInterface, + d2d_device_context_inner_AddRef, + d2d_device_context_inner_Release, +}; + +static HRESULT STDMETHODCALLTYPE d2d_device_context_QueryInterface(ID2D1DeviceContext1 *iface, REFIID iid, void **out) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + return IUnknown_QueryInterface(context->outer_unknown, iid, out); +} + +static ULONG STDMETHODCALLTYPE d2d_device_context_AddRef(ID2D1DeviceContext1 *iface) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p.\n", iface); + + return IUnknown_AddRef(context->outer_unknown); +} + +static ULONG STDMETHODCALLTYPE d2d_device_context_Release(ID2D1DeviceContext1 *iface) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p.\n", iface); + + return IUnknown_Release(context->outer_unknown); +} + +static void STDMETHODCALLTYPE d2d_device_context_GetFactory(ID2D1DeviceContext1 *iface, ID2D1Factory **factory) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, factory %p.\n", iface, factory); + + *factory = render_target->factory; + ID2D1Factory_AddRef(*factory); +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateBitmap(ID2D1DeviceContext1 *iface, + D2D1_SIZE_U size, const void *src_data, UINT32 pitch, const D2D1_BITMAP_PROPERTIES *desc, ID2D1Bitmap **bitmap) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + D2D1_BITMAP_PROPERTIES1 bitmap_desc; + struct d2d_bitmap *object; + HRESULT hr; + + TRACE("iface %p, size {%u, %u}, src_data %p, pitch %u, desc %p, bitmap %p.\n", + iface, size.width, size.height, src_data, pitch, desc, bitmap); + + if (desc) + { + memcpy(&bitmap_desc, desc, sizeof(*desc)); + bitmap_desc.bitmapOptions = 0; + bitmap_desc.colorContext = NULL; + } + + if (SUCCEEDED(hr = d2d_bitmap_create(context, size, src_data, pitch, desc ? &bitmap_desc : NULL, &object))) + *bitmap = (ID2D1Bitmap *)&object->ID2D1Bitmap1_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateBitmapFromWicBitmap(ID2D1DeviceContext1 *iface, + IWICBitmapSource *bitmap_source, const D2D1_BITMAP_PROPERTIES *desc, ID2D1Bitmap **bitmap) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + D2D1_BITMAP_PROPERTIES1 bitmap_desc; + struct d2d_bitmap *object; + HRESULT hr; + + TRACE("iface %p, bitmap_source %p, desc %p, bitmap %p.\n", + iface, bitmap_source, desc, bitmap); + + if (desc) + { + memcpy(&bitmap_desc, desc, sizeof(*desc)); + bitmap_desc.bitmapOptions = 0; + bitmap_desc.colorContext = NULL; + } + + if (SUCCEEDED(hr = d2d_bitmap_create_from_wic_bitmap(context, bitmap_source, desc ? &bitmap_desc : NULL, &object))) + *bitmap = (ID2D1Bitmap *)&object->ID2D1Bitmap1_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateSharedBitmap(ID2D1DeviceContext1 *iface, + REFIID iid, void *data, const D2D1_BITMAP_PROPERTIES *desc, ID2D1Bitmap **bitmap) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + D2D1_BITMAP_PROPERTIES1 bitmap_desc; + struct d2d_bitmap *object; + HRESULT hr; + + TRACE("iface %p, iid %s, data %p, desc %p, bitmap %p.\n", + iface, debugstr_guid(iid), data, desc, bitmap); + + if (desc) + { + memcpy(&bitmap_desc, desc, sizeof(*desc)); + if (IsEqualIID(iid, &IID_IDXGISurface) || IsEqualIID(iid, &IID_IDXGISurface1)) + bitmap_desc.bitmapOptions = d2d_get_bitmap_options_for_surface(data); + else + bitmap_desc.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW; + bitmap_desc.colorContext = NULL; + } + + if (SUCCEEDED(hr = d2d_bitmap_create_shared(context, iid, data, desc ? &bitmap_desc : NULL, &object))) + *bitmap = (ID2D1Bitmap *)&object->ID2D1Bitmap1_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateBitmapBrush(ID2D1DeviceContext1 *iface, + ID2D1Bitmap *bitmap, const D2D1_BITMAP_BRUSH_PROPERTIES *bitmap_brush_desc, + const D2D1_BRUSH_PROPERTIES *brush_desc, ID2D1BitmapBrush **brush) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_brush *object; + HRESULT hr; + + TRACE("iface %p, bitmap %p, bitmap_brush_desc %p, brush_desc %p, brush %p.\n", + iface, bitmap, bitmap_brush_desc, brush_desc, brush); + + if (SUCCEEDED(hr = d2d_bitmap_brush_create(context->factory, bitmap, (const D2D1_BITMAP_BRUSH_PROPERTIES1 *)bitmap_brush_desc, + brush_desc, &object))) + *brush = (ID2D1BitmapBrush *)&object->ID2D1Brush_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateSolidColorBrush(ID2D1DeviceContext1 *iface, + const D2D1_COLOR_F *color, const D2D1_BRUSH_PROPERTIES *desc, ID2D1SolidColorBrush **brush) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + struct d2d_brush *object; + HRESULT hr; + + TRACE("iface %p, color %p, desc %p, brush %p.\n", iface, color, desc, brush); + + if (SUCCEEDED(hr = d2d_solid_color_brush_create(render_target->factory, color, desc, &object))) + *brush = (ID2D1SolidColorBrush *)&object->ID2D1Brush_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateGradientStopCollection(ID2D1DeviceContext1 *iface, + const D2D1_GRADIENT_STOP *stops, UINT32 stop_count, D2D1_GAMMA gamma, D2D1_EXTEND_MODE extend_mode, + ID2D1GradientStopCollection **gradient) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + struct d2d_gradient *object; + HRESULT hr; + + TRACE("iface %p, stops %p, stop_count %u, gamma %#x, extend_mode %#x, gradient %p.\n", + iface, stops, stop_count, gamma, extend_mode, gradient); + + if (SUCCEEDED(hr = d2d_gradient_create(render_target->factory, render_target->d3d_device, + stops, stop_count, gamma, extend_mode, &object))) + *gradient = &object->ID2D1GradientStopCollection_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateLinearGradientBrush(ID2D1DeviceContext1 *iface, + const D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES *gradient_brush_desc, const D2D1_BRUSH_PROPERTIES *brush_desc, + ID2D1GradientStopCollection *gradient, ID2D1LinearGradientBrush **brush) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + struct d2d_brush *object; + HRESULT hr; + + TRACE("iface %p, gradient_brush_desc %p, brush_desc %p, gradient %p, brush %p.\n", + iface, gradient_brush_desc, brush_desc, gradient, brush); + + if (SUCCEEDED(hr = d2d_linear_gradient_brush_create(render_target->factory, gradient_brush_desc, brush_desc, + gradient, &object))) + *brush = (ID2D1LinearGradientBrush *)&object->ID2D1Brush_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateRadialGradientBrush(ID2D1DeviceContext1 *iface, + const D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES *gradient_brush_desc, const D2D1_BRUSH_PROPERTIES *brush_desc, + ID2D1GradientStopCollection *gradient, ID2D1RadialGradientBrush **brush) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + struct d2d_brush *object; + HRESULT hr; + + TRACE("iface %p, gradient_brush_desc %p, brush_desc %p, gradient %p, brush %p.\n", + iface, gradient_brush_desc, brush_desc, gradient, brush); + + if (SUCCEEDED(hr = d2d_radial_gradient_brush_create(render_target->factory, + gradient_brush_desc, brush_desc, gradient, &object))) + *brush = (ID2D1RadialGradientBrush *)&object->ID2D1Brush_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateCompatibleRenderTarget(ID2D1DeviceContext1 *iface, + const D2D1_SIZE_F *size, const D2D1_SIZE_U *pixel_size, const D2D1_PIXEL_FORMAT *format, + D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS options, ID2D1BitmapRenderTarget **rt) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + struct d2d_bitmap_render_target *object; + HRESULT hr; + + TRACE("iface %p, size %p, pixel_size %p, format %p, options %#x, render_target %p.\n", + iface, size, pixel_size, format, options, rt); + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = d2d_bitmap_render_target_init(object, render_target, size, pixel_size, + format, options))) + { + WARN("Failed to initialise render target, hr %#lx.\n", hr); + free(object); + return hr; + } + + TRACE("Created render target %p.\n", object); + *rt = &object->ID2D1BitmapRenderTarget_iface; + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateLayer(ID2D1DeviceContext1 *iface, + const D2D1_SIZE_F *size, ID2D1Layer **layer) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + struct d2d_layer *object; + HRESULT hr; + + TRACE("iface %p, size %p, layer %p.\n", iface, size, layer); + + if (SUCCEEDED(hr = d2d_layer_create(render_target->factory, size, &object))) + *layer = &object->ID2D1Layer_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateMesh(ID2D1DeviceContext1 *iface, ID2D1Mesh **mesh) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + struct d2d_mesh *object; + HRESULT hr; + + TRACE("iface %p, mesh %p.\n", iface, mesh); + + if (SUCCEEDED(hr = d2d_mesh_create(render_target->factory, &object))) + *mesh = &object->ID2D1Mesh_iface; + + return hr; +} + +static void STDMETHODCALLTYPE d2d_device_context_DrawLine(ID2D1DeviceContext1 *iface, + D2D1_POINT_2F p0, D2D1_POINT_2F p1, ID2D1Brush *brush, float stroke_width, ID2D1StrokeStyle *stroke_style) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + ID2D1PathGeometry *geometry; + ID2D1GeometrySink *sink; + HRESULT hr; + + TRACE("iface %p, p0 %s, p1 %s, brush %p, stroke_width %.8e, stroke_style %p.\n", + iface, debug_d2d_point_2f(&p0), debug_d2d_point_2f(&p1), brush, stroke_width, stroke_style); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + d2d_command_list_draw_line(context->target.command_list, context, p0, p1, brush, stroke_width, stroke_style); + return; + } + + if (FAILED(hr = ID2D1Factory_CreatePathGeometry(context->factory, &geometry))) + { + WARN("Failed to create path geometry, hr %#lx.\n", hr); + return; + } + + if (FAILED(hr = ID2D1PathGeometry_Open(geometry, &sink))) + { + WARN("Failed to open geometry sink, hr %#lx.\n", hr); + ID2D1PathGeometry_Release(geometry); + return; + } + + ID2D1GeometrySink_BeginFigure(sink, p0, D2D1_FIGURE_BEGIN_HOLLOW); + ID2D1GeometrySink_AddLine(sink, p1); + ID2D1GeometrySink_EndFigure(sink, D2D1_FIGURE_END_OPEN); + if (FAILED(hr = ID2D1GeometrySink_Close(sink))) + WARN("Failed to close geometry sink, hr %#lx.\n", hr); + ID2D1GeometrySink_Release(sink); + + ID2D1DeviceContext1_DrawGeometry(iface, (ID2D1Geometry *)geometry, brush, stroke_width, stroke_style); + ID2D1PathGeometry_Release(geometry); +} + +static void STDMETHODCALLTYPE d2d_device_context_DrawRectangle(ID2D1DeviceContext1 *iface, + const D2D1_RECT_F *rect, ID2D1Brush *brush, float stroke_width, ID2D1StrokeStyle *stroke_style) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + ID2D1RectangleGeometry *geometry; + HRESULT hr; + + TRACE("iface %p, rect %s, brush %p, stroke_width %.8e, stroke_style %p.\n", + iface, debug_d2d_rect_f(rect), brush, stroke_width, stroke_style); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + d2d_command_list_draw_rectangle(context->target.command_list, context, rect, brush, stroke_width, stroke_style); + return; + } + + if (FAILED(hr = ID2D1Factory_CreateRectangleGeometry(context->factory, rect, &geometry))) + { + ERR("Failed to create geometry, hr %#lx.\n", hr); + return; + } + + ID2D1DeviceContext1_DrawGeometry(iface, (ID2D1Geometry *)geometry, brush, stroke_width, stroke_style); + ID2D1RectangleGeometry_Release(geometry); +} + +static void STDMETHODCALLTYPE d2d_device_context_FillRectangle(ID2D1DeviceContext1 *iface, + const D2D1_RECT_F *rect, ID2D1Brush *brush) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + ID2D1RectangleGeometry *geometry; + HRESULT hr; + + TRACE("iface %p, rect %s, brush %p.\n", iface, debug_d2d_rect_f(rect), brush); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + d2d_command_list_fill_rectangle(context->target.command_list, context, rect, brush); + return; + } + + if (FAILED(hr = ID2D1Factory_CreateRectangleGeometry(context->factory, rect, &geometry))) + { + ERR("Failed to create geometry, hr %#lx.\n", hr); + return; + } + + ID2D1DeviceContext1_FillGeometry(iface, (ID2D1Geometry *)geometry, brush, NULL); + ID2D1RectangleGeometry_Release(geometry); +} + +static void STDMETHODCALLTYPE d2d_device_context_DrawRoundedRectangle(ID2D1DeviceContext1 *iface, + const D2D1_ROUNDED_RECT *rect, ID2D1Brush *brush, float stroke_width, ID2D1StrokeStyle *stroke_style) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + ID2D1RoundedRectangleGeometry *geometry; + HRESULT hr; + + TRACE("iface %p, rect %p, brush %p, stroke_width %.8e, stroke_style %p.\n", + iface, rect, brush, stroke_width, stroke_style); + + if (FAILED(hr = ID2D1Factory_CreateRoundedRectangleGeometry(render_target->factory, rect, &geometry))) + { + ERR("Failed to create geometry, hr %#lx.\n", hr); + return; + } + + ID2D1DeviceContext1_DrawGeometry(iface, (ID2D1Geometry *)geometry, brush, stroke_width, stroke_style); + ID2D1RoundedRectangleGeometry_Release(geometry); +} + +static void STDMETHODCALLTYPE d2d_device_context_FillRoundedRectangle(ID2D1DeviceContext1 *iface, + const D2D1_ROUNDED_RECT *rect, ID2D1Brush *brush) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + ID2D1RoundedRectangleGeometry *geometry; + HRESULT hr; + + TRACE("iface %p, rect %p, brush %p.\n", iface, rect, brush); + + if (FAILED(hr = ID2D1Factory_CreateRoundedRectangleGeometry(render_target->factory, rect, &geometry))) + { + ERR("Failed to create geometry, hr %#lx.\n", hr); + return; + } + + ID2D1DeviceContext1_FillGeometry(iface, (ID2D1Geometry *)geometry, brush, NULL); + ID2D1RoundedRectangleGeometry_Release(geometry); +} + +static void STDMETHODCALLTYPE d2d_device_context_DrawEllipse(ID2D1DeviceContext1 *iface, + const D2D1_ELLIPSE *ellipse, ID2D1Brush *brush, float stroke_width, ID2D1StrokeStyle *stroke_style) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + ID2D1EllipseGeometry *geometry; + HRESULT hr; + + TRACE("iface %p, ellipse %p, brush %p, stroke_width %.8e, stroke_style %p.\n", + iface, ellipse, brush, stroke_width, stroke_style); + + if (FAILED(hr = ID2D1Factory_CreateEllipseGeometry(render_target->factory, ellipse, &geometry))) + { + ERR("Failed to create geometry, hr %#lx.\n", hr); + return; + } + + ID2D1DeviceContext1_DrawGeometry(iface, (ID2D1Geometry *)geometry, brush, stroke_width, stroke_style); + ID2D1EllipseGeometry_Release(geometry); +} + +static void STDMETHODCALLTYPE d2d_device_context_FillEllipse(ID2D1DeviceContext1 *iface, + const D2D1_ELLIPSE *ellipse, ID2D1Brush *brush) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + ID2D1EllipseGeometry *geometry; + HRESULT hr; + + TRACE("iface %p, ellipse %p, brush %p.\n", iface, ellipse, brush); + + if (FAILED(hr = ID2D1Factory_CreateEllipseGeometry(render_target->factory, ellipse, &geometry))) + { + ERR("Failed to create geometry, hr %#lx.\n", hr); + return; + } + + ID2D1DeviceContext1_FillGeometry(iface, (ID2D1Geometry *)geometry, brush, NULL); + ID2D1EllipseGeometry_Release(geometry); +} + +static HRESULT d2d_device_context_update_ps_cb(struct d2d_device_context *context, + struct d2d_brush *brush, struct d2d_brush *opacity_brush, BOOL outline, BOOL is_arc) +{ + D3D11_MAPPED_SUBRESOURCE map_desc; + ID3D11DeviceContext *d3d_context; + struct d2d_ps_cb *cb_data; + HRESULT hr; + + ID3D11Device1_GetImmediateContext(context->d3d_device, &d3d_context); + + if (FAILED(hr = ID3D11DeviceContext_Map(d3d_context, (ID3D11Resource *)context->ps_cb, + 0, D3D11_MAP_WRITE_DISCARD, 0, &map_desc))) + { + WARN("Failed to map constant buffer, hr %#lx.\n", hr); + ID3D11DeviceContext_Release(d3d_context); + return hr; + } + + cb_data = map_desc.pData; + cb_data->outline = outline; + cb_data->is_arc = is_arc; + cb_data->pad[0] = 0; + cb_data->pad[1] = 0; + if (!d2d_brush_fill_cb(brush, &cb_data->colour_brush)) + WARN("Failed to initialize colour brush buffer.\n"); + if (!d2d_brush_fill_cb(opacity_brush, &cb_data->opacity_brush)) + WARN("Failed to initialize opacity brush buffer.\n"); + + ID3D11DeviceContext_Unmap(d3d_context, (ID3D11Resource *)context->ps_cb, 0); + ID3D11DeviceContext_Release(d3d_context); + + return hr; +} + +static HRESULT d2d_device_context_update_vs_cb(struct d2d_device_context *context, + const D2D_MATRIX_3X2_F *geometry_transform, float stroke_width) +{ + D3D11_MAPPED_SUBRESOURCE map_desc; + ID3D11DeviceContext *d3d_context; + const D2D1_MATRIX_3X2_F *w; + struct d2d_vs_cb *cb_data; + float tmp_x, tmp_y; + HRESULT hr; + + ID3D11Device1_GetImmediateContext(context->d3d_device, &d3d_context); + + if (FAILED(hr = ID3D11DeviceContext_Map(d3d_context, (ID3D11Resource *)context->vs_cb, + 0, D3D11_MAP_WRITE_DISCARD, 0, &map_desc))) + { + WARN("Failed to map constant buffer, hr %#lx.\n", hr); + ID3D11DeviceContext_Release(d3d_context); + return hr; + } + + cb_data = map_desc.pData; + cb_data->transform_geometry._11 = geometry_transform->_11; + cb_data->transform_geometry._21 = geometry_transform->_21; + cb_data->transform_geometry._31 = geometry_transform->_31; + cb_data->transform_geometry.pad0 = 0.0f; + cb_data->transform_geometry._12 = geometry_transform->_12; + cb_data->transform_geometry._22 = geometry_transform->_22; + cb_data->transform_geometry._32 = geometry_transform->_32; + cb_data->transform_geometry.stroke_width = stroke_width; + + w = &context->drawing_state.transform; + + tmp_x = context->desc.dpiX / 96.0f; + cb_data->transform_rtx.x = w->_11 * tmp_x; + cb_data->transform_rtx.y = w->_21 * tmp_x; + cb_data->transform_rtx.z = w->_31 * tmp_x; + cb_data->transform_rtx.w = 2.0f / context->pixel_size.width; + + tmp_y = context->desc.dpiY / 96.0f; + cb_data->transform_rty.x = w->_12 * tmp_y; + cb_data->transform_rty.y = w->_22 * tmp_y; + cb_data->transform_rty.z = w->_32 * tmp_y; + cb_data->transform_rty.w = -2.0f / context->pixel_size.height; + + ID3D11DeviceContext_Unmap(d3d_context, (ID3D11Resource *)context->vs_cb, 0); + ID3D11DeviceContext_Release(d3d_context); + + return S_OK; +} + +static void d2d_device_context_draw_geometry(struct d2d_device_context *render_target, + const struct d2d_geometry *geometry, struct d2d_brush *brush, float stroke_width) +{ + D3D11_SUBRESOURCE_DATA buffer_data; + D3D11_BUFFER_DESC buffer_desc; + ID3D11Buffer *ib, *vb; + HRESULT hr; + + if (FAILED(hr = d2d_device_context_update_vs_cb(render_target, &geometry->transform, stroke_width))) + { + WARN("Failed to update vs constant buffer, hr %#lx.\n", hr); + return; + } + + if (FAILED(hr = d2d_device_context_update_ps_cb(render_target, brush, NULL, TRUE, FALSE))) + { + WARN("Failed to update ps constant buffer, hr %#lx.\n", hr); + return; + } + + buffer_desc.Usage = D3D11_USAGE_DEFAULT; + buffer_desc.CPUAccessFlags = 0; + buffer_desc.MiscFlags = 0; + + buffer_data.SysMemPitch = 0; + buffer_data.SysMemSlicePitch = 0; + + if (geometry->outline.face_count) + { + buffer_desc.ByteWidth = geometry->outline.face_count * sizeof(*geometry->outline.faces); + buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER; + buffer_data.pSysMem = geometry->outline.faces; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, &buffer_desc, &buffer_data, &ib))) + { + WARN("Failed to create index buffer, hr %#lx.\n", hr); + return; + } + + buffer_desc.ByteWidth = geometry->outline.vertex_count * sizeof(*geometry->outline.vertices); + buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + buffer_data.pSysMem = geometry->outline.vertices; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, &buffer_desc, &buffer_data, &vb))) + { + ERR("Failed to create vertex buffer, hr %#lx.\n", hr); + ID3D11Buffer_Release(ib); + return; + } + + d2d_device_context_draw(render_target, D2D_SHAPE_TYPE_OUTLINE, ib, 3 * geometry->outline.face_count, vb, + sizeof(*geometry->outline.vertices), brush, NULL); + + ID3D11Buffer_Release(vb); + ID3D11Buffer_Release(ib); + } + + if (geometry->outline.bezier_face_count) + { + buffer_desc.ByteWidth = geometry->outline.bezier_face_count * sizeof(*geometry->outline.bezier_faces); + buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER; + buffer_data.pSysMem = geometry->outline.bezier_faces; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, &buffer_desc, &buffer_data, &ib))) + { + WARN("Failed to create curves index buffer, hr %#lx.\n", hr); + return; + } + + buffer_desc.ByteWidth = geometry->outline.bezier_count * sizeof(*geometry->outline.beziers); + buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + buffer_data.pSysMem = geometry->outline.beziers; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, &buffer_desc, &buffer_data, &vb))) + { + ERR("Failed to create curves vertex buffer, hr %#lx.\n", hr); + ID3D11Buffer_Release(ib); + return; + } + + d2d_device_context_draw(render_target, D2D_SHAPE_TYPE_BEZIER_OUTLINE, ib, + 3 * geometry->outline.bezier_face_count, vb, + sizeof(*geometry->outline.beziers), brush, NULL); + + ID3D11Buffer_Release(vb); + ID3D11Buffer_Release(ib); + } + + if (geometry->outline.arc_face_count) + { + buffer_desc.ByteWidth = geometry->outline.arc_face_count * sizeof(*geometry->outline.arc_faces); + buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER; + buffer_data.pSysMem = geometry->outline.arc_faces; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, &buffer_desc, &buffer_data, &ib))) + { + WARN("Failed to create arcs index buffer, hr %#lx.\n", hr); + return; + } + + buffer_desc.ByteWidth = geometry->outline.arc_count * sizeof(*geometry->outline.arcs); + buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + buffer_data.pSysMem = geometry->outline.arcs; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, &buffer_desc, &buffer_data, &vb))) + { + ERR("Failed to create arcs vertex buffer, hr %#lx.\n", hr); + ID3D11Buffer_Release(ib); + return; + } + + if (SUCCEEDED(d2d_device_context_update_ps_cb(render_target, brush, NULL, TRUE, TRUE))) + d2d_device_context_draw(render_target, D2D_SHAPE_TYPE_ARC_OUTLINE, ib, + 3 * geometry->outline.arc_face_count, vb, + sizeof(*geometry->outline.arcs), brush, NULL); + + ID3D11Buffer_Release(vb); + ID3D11Buffer_Release(ib); + } +} + +static void STDMETHODCALLTYPE d2d_device_context_DrawGeometry(ID2D1DeviceContext1 *iface, + ID2D1Geometry *geometry, ID2D1Brush *brush, float stroke_width, ID2D1StrokeStyle *stroke_style) +{ + const struct d2d_geometry *geometry_impl = unsafe_impl_from_ID2D1Geometry(geometry); + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_brush *brush_impl = unsafe_impl_from_ID2D1Brush(brush); + + TRACE("iface %p, geometry %p, brush %p, stroke_width %.8e, stroke_style %p.\n", + iface, geometry, brush, stroke_width, stroke_style); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + d2d_command_list_draw_geometry(context->target.command_list, context, geometry, brush, + stroke_width, stroke_style); + return; + } + + if (stroke_style) + FIXME("Ignoring stroke style %p.\n", stroke_style); + + d2d_device_context_draw_geometry(context, geometry_impl, brush_impl, stroke_width); +} + +static void d2d_device_context_fill_geometry(struct d2d_device_context *render_target, + const struct d2d_geometry *geometry, struct d2d_brush *brush, struct d2d_brush *opacity_brush) +{ + D3D11_SUBRESOURCE_DATA buffer_data; + D3D11_BUFFER_DESC buffer_desc; + ID3D11Buffer *ib, *vb; + HRESULT hr; + + buffer_desc.Usage = D3D11_USAGE_DEFAULT; + buffer_desc.CPUAccessFlags = 0; + buffer_desc.MiscFlags = 0; + + buffer_data.SysMemPitch = 0; + buffer_data.SysMemSlicePitch = 0; + + if (FAILED(hr = d2d_device_context_update_vs_cb(render_target, &geometry->transform, 0.0f))) + { + WARN("Failed to update vs constant buffer, hr %#lx.\n", hr); + return; + } + + if (FAILED(hr = d2d_device_context_update_ps_cb(render_target, brush, opacity_brush, FALSE, FALSE))) + { + WARN("Failed to update ps constant buffer, hr %#lx.\n", hr); + return; + } + + if (geometry->fill.face_count) + { + buffer_desc.ByteWidth = geometry->fill.face_count * sizeof(*geometry->fill.faces); + buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER; + buffer_data.pSysMem = geometry->fill.faces; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, &buffer_desc, &buffer_data, &ib))) + { + WARN("Failed to create index buffer, hr %#lx.\n", hr); + return; + } + + buffer_desc.ByteWidth = geometry->fill.vertex_count * sizeof(*geometry->fill.vertices); + buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + buffer_data.pSysMem = geometry->fill.vertices; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, &buffer_desc, &buffer_data, &vb))) + { + ERR("Failed to create vertex buffer, hr %#lx.\n", hr); + ID3D11Buffer_Release(ib); + return; + } + + d2d_device_context_draw(render_target, D2D_SHAPE_TYPE_TRIANGLE, ib, 3 * geometry->fill.face_count, vb, + sizeof(*geometry->fill.vertices), brush, opacity_brush); + + ID3D11Buffer_Release(vb); + ID3D11Buffer_Release(ib); + } + + if (geometry->fill.bezier_vertex_count) + { + buffer_desc.ByteWidth = geometry->fill.bezier_vertex_count * sizeof(*geometry->fill.bezier_vertices); + buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + buffer_data.pSysMem = geometry->fill.bezier_vertices; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, &buffer_desc, &buffer_data, &vb))) + { + ERR("Failed to create curves vertex buffer, hr %#lx.\n", hr); + return; + } + + d2d_device_context_draw(render_target, D2D_SHAPE_TYPE_CURVE, NULL, geometry->fill.bezier_vertex_count, vb, + sizeof(*geometry->fill.bezier_vertices), brush, opacity_brush); + + ID3D11Buffer_Release(vb); + } + + if (geometry->fill.arc_vertex_count) + { + buffer_desc.ByteWidth = geometry->fill.arc_vertex_count * sizeof(*geometry->fill.arc_vertices); + buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + buffer_data.pSysMem = geometry->fill.arc_vertices; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, &buffer_desc, &buffer_data, &vb))) + { + ERR("Failed to create arc vertex buffer, hr %#lx.\n", hr); + return; + } + + if (SUCCEEDED(d2d_device_context_update_ps_cb(render_target, brush, opacity_brush, FALSE, TRUE))) + d2d_device_context_draw(render_target, D2D_SHAPE_TYPE_CURVE, NULL, geometry->fill.arc_vertex_count, vb, + sizeof(*geometry->fill.arc_vertices), brush, opacity_brush); + + ID3D11Buffer_Release(vb); + } +} + +static void STDMETHODCALLTYPE d2d_device_context_FillGeometry(ID2D1DeviceContext1 *iface, + ID2D1Geometry *geometry, ID2D1Brush *brush, ID2D1Brush *opacity_brush) +{ + const struct d2d_geometry *geometry_impl = unsafe_impl_from_ID2D1Geometry(geometry); + struct d2d_brush *opacity_brush_impl = unsafe_impl_from_ID2D1Brush(opacity_brush); + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_brush *brush_impl = unsafe_impl_from_ID2D1Brush(brush); + + TRACE("iface %p, geometry %p, brush %p, opacity_brush %p.\n", iface, geometry, brush, opacity_brush); + + if (FAILED(context->error.code)) + return; + + if (opacity_brush && brush_impl->type != D2D_BRUSH_TYPE_BITMAP) + { + d2d_device_context_set_error(context, D2DERR_INCOMPATIBLE_BRUSH_TYPES); + return; + } + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_fill_geometry(context->target.command_list, context, geometry, brush, opacity_brush); + else + d2d_device_context_fill_geometry(context, geometry_impl, brush_impl, opacity_brush_impl); +} + +static void STDMETHODCALLTYPE d2d_device_context_FillMesh(ID2D1DeviceContext1 *iface, + ID2D1Mesh *mesh, ID2D1Brush *brush) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + FIXME("iface %p, mesh %p, brush %p stub!\n", iface, mesh, brush); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_fill_mesh(context->target.command_list, context, mesh, brush); +} + +static void STDMETHODCALLTYPE d2d_device_context_FillOpacityMask(ID2D1DeviceContext1 *iface, + ID2D1Bitmap *mask, ID2D1Brush *brush, D2D1_OPACITY_MASK_CONTENT content, + const D2D1_RECT_F *dst_rect, const D2D1_RECT_F *src_rect) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + FIXME("iface %p, mask %p, brush %p, content %#x, dst_rect %s, src_rect %s stub!\n", + iface, mask, brush, content, debug_d2d_rect_f(dst_rect), debug_d2d_rect_f(src_rect)); + + if (FAILED(context->error.code)) + return; + + if (context->drawing_state.antialiasMode != D2D1_ANTIALIAS_MODE_ALIASED) + { + d2d_device_context_set_error(context, D2DERR_WRONG_STATE); + return; + } + + if ((unsigned int)content > D2D1_OPACITY_MASK_CONTENT_TEXT_GDI_COMPATIBLE) + { + d2d_device_context_set_error(context, E_INVALIDARG); + return; + } + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_fill_opacity_mask(context->target.command_list, context, mask, brush, dst_rect, src_rect); +} + +static void d2d_device_context_draw_bitmap(struct d2d_device_context *context, ID2D1Bitmap *bitmap, + const D2D1_RECT_F *dst_rect, float opacity, D2D1_INTERPOLATION_MODE interpolation_mode, + const D2D1_RECT_F *src_rect, const D2D1_POINT_2F *offset, + const D2D1_MATRIX_4X4_F *perspective_transform) +{ + D2D1_BITMAP_BRUSH_PROPERTIES1 bitmap_brush_desc; + D2D1_BRUSH_PROPERTIES brush_desc; + struct d2d_brush *brush; + D2D1_SIZE_F size; + D2D1_RECT_F s, d; + HRESULT hr; + + if (perspective_transform) + FIXME("Perspective transform is ignored.\n"); + + size = ID2D1Bitmap_GetSize(bitmap); + d2d_rect_set(&s, 0.0f, 0.0f, size.width, size.height); + if (src_rect && src_rect->left <= src_rect->right + && src_rect->top <= src_rect->bottom) + { + d2d_rect_intersect(&s, src_rect); + } + + if (s.left == s.right || s.top == s.bottom) + return; + + if (dst_rect) + { + d = *dst_rect; + } + else + { + d.left = 0.0f; + d.top = 0.0f; + d.right = s.right - s.left; + d.bottom = s.bottom - s.top; + } + + if (offset) + { + d.left += offset->x; + d.top += offset->y; + d.right += offset->x; + d.bottom += offset->y; + } + + bitmap_brush_desc.extendModeX = D2D1_EXTEND_MODE_CLAMP; + bitmap_brush_desc.extendModeY = D2D1_EXTEND_MODE_CLAMP; + bitmap_brush_desc.interpolationMode = interpolation_mode; + + brush_desc.opacity = opacity; + brush_desc.transform._11 = fabsf((d.right - d.left) / (s.right - s.left)); + brush_desc.transform._21 = 0.0f; + brush_desc.transform._31 = min(d.left, d.right) - min(s.left, s.right) * brush_desc.transform._11; + brush_desc.transform._12 = 0.0f; + brush_desc.transform._22 = fabsf((d.bottom - d.top) / (s.bottom - s.top)); + brush_desc.transform._32 = min(d.top, d.bottom) - min(s.top, s.bottom) * brush_desc.transform._22; + + if (FAILED(hr = d2d_bitmap_brush_create(context->factory, bitmap, &bitmap_brush_desc, &brush_desc, &brush))) + { + ERR("Failed to create bitmap brush, hr %#lx.\n", hr); + return; + } + + d2d_device_context_FillRectangle(&context->ID2D1DeviceContext1_iface, &d, &brush->ID2D1Brush_iface); + ID2D1Brush_Release(&brush->ID2D1Brush_iface); +} + +static void STDMETHODCALLTYPE d2d_device_context_DrawBitmap(ID2D1DeviceContext1 *iface, + ID2D1Bitmap *bitmap, const D2D1_RECT_F *dst_rect, float opacity, + D2D1_BITMAP_INTERPOLATION_MODE interpolation_mode, const D2D1_RECT_F *src_rect) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, bitmap %p, dst_rect %s, opacity %.8e, interpolation_mode %#x, src_rect %s.\n", + iface, bitmap, debug_d2d_rect_f(dst_rect), opacity, interpolation_mode, debug_d2d_rect_f(src_rect)); + + if (interpolation_mode != D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR + && interpolation_mode != D2D1_BITMAP_INTERPOLATION_MODE_LINEAR) + { + d2d_device_context_set_error(context, E_INVALIDARG); + return; + } + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + d2d_command_list_draw_bitmap(context->target.command_list, bitmap, dst_rect, opacity, + d2d1_1_interp_mode_from_d2d1(interpolation_mode), src_rect, NULL); + } + else + { + d2d_device_context_draw_bitmap(context, bitmap, dst_rect, opacity, + d2d1_1_interp_mode_from_d2d1(interpolation_mode), src_rect, NULL, NULL); + } +} + +static void STDMETHODCALLTYPE d2d_device_context_DrawText(ID2D1DeviceContext1 *iface, + const WCHAR *string, UINT32 string_len, IDWriteTextFormat *text_format, const D2D1_RECT_F *layout_rect, + ID2D1Brush *brush, D2D1_DRAW_TEXT_OPTIONS options, DWRITE_MEASURING_MODE measuring_mode) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + IDWriteTextLayout *text_layout; + IDWriteFactory *dwrite_factory; + D2D1_POINT_2F origin; + float width, height; + HRESULT hr; + + TRACE("iface %p, string %s, string_len %u, text_format %p, layout_rect %s, " + "brush %p, options %#x, measuring_mode %#x.\n", + iface, debugstr_wn(string, string_len), string_len, text_format, debug_d2d_rect_f(layout_rect), + brush, options, measuring_mode); + + if (FAILED(hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, + &IID_IDWriteFactory, (IUnknown **)&dwrite_factory))) + { + ERR("Failed to create dwrite factory, hr %#lx.\n", hr); + return; + } + + width = max(0.0f, layout_rect->right - layout_rect->left); + height = max(0.0f, layout_rect->bottom - layout_rect->top); + if (measuring_mode == DWRITE_MEASURING_MODE_NATURAL) + hr = IDWriteFactory_CreateTextLayout(dwrite_factory, string, string_len, text_format, + width, height, &text_layout); + else + hr = IDWriteFactory_CreateGdiCompatibleTextLayout(dwrite_factory, string, string_len, text_format, + width, height, render_target->desc.dpiX / 96.0f, (DWRITE_MATRIX *)&render_target->drawing_state.transform, + measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL, &text_layout); + IDWriteFactory_Release(dwrite_factory); + if (FAILED(hr)) + { + ERR("Failed to create text layout, hr %#lx.\n", hr); + return; + } + + d2d_point_set(&origin, min(layout_rect->left, layout_rect->right), min(layout_rect->top, layout_rect->bottom)); + ID2D1DeviceContext1_DrawTextLayout(iface, origin, text_layout, brush, options); + IDWriteTextLayout_Release(text_layout); +} + +static void STDMETHODCALLTYPE d2d_device_context_DrawTextLayout(ID2D1DeviceContext1 *iface, + D2D1_POINT_2F origin, IDWriteTextLayout *layout, ID2D1Brush *brush, D2D1_DRAW_TEXT_OPTIONS options) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + struct d2d_draw_text_layout_ctx ctx; + HRESULT hr; + + TRACE("iface %p, origin %s, layout %p, brush %p, options %#x.\n", + iface, debug_d2d_point_2f(&origin), layout, brush, options); + + ctx.brush = brush; + ctx.options = options; + + if (FAILED(hr = IDWriteTextLayout_Draw(layout, + &ctx, &render_target->IDWriteTextRenderer_iface, origin.x, origin.y))) + FIXME("Failed to draw text layout, hr %#lx.\n", hr); +} + +static D2D1_ANTIALIAS_MODE d2d_device_context_set_aa_mode_from_text_aa_mode(struct d2d_device_context *rt) +{ + D2D1_ANTIALIAS_MODE prev_antialias_mode = rt->drawing_state.antialiasMode; + rt->drawing_state.antialiasMode = rt->drawing_state.textAntialiasMode == D2D1_TEXT_ANTIALIAS_MODE_ALIASED ? + D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; + return prev_antialias_mode; +} + +static void d2d_device_context_draw_glyph_run_outline(struct d2d_device_context *render_target, + D2D1_POINT_2F baseline_origin, const DWRITE_GLYPH_RUN *glyph_run, ID2D1Brush *brush) +{ + D2D1_MATRIX_3X2_F *transform, prev_transform; + D2D1_ANTIALIAS_MODE prev_antialias_mode; + ID2D1PathGeometry *geometry; + ID2D1GeometrySink *sink; + HRESULT hr; + + if (FAILED(hr = ID2D1Factory_CreatePathGeometry(render_target->factory, &geometry))) + { + ERR("Failed to create geometry, hr %#lx.\n", hr); + return; + } + + if (FAILED(hr = ID2D1PathGeometry_Open(geometry, &sink))) + { + ERR("Failed to open geometry sink, hr %#lx.\n", hr); + ID2D1PathGeometry_Release(geometry); + return; + } + + if (FAILED(hr = IDWriteFontFace_GetGlyphRunOutline(glyph_run->fontFace, glyph_run->fontEmSize, + glyph_run->glyphIndices, glyph_run->glyphAdvances, glyph_run->glyphOffsets, glyph_run->glyphCount, + glyph_run->isSideways, glyph_run->bidiLevel & 1, (IDWriteGeometrySink *)sink))) + { + ERR("Failed to get glyph run outline, hr %#lx.\n", hr); + ID2D1GeometrySink_Release(sink); + ID2D1PathGeometry_Release(geometry); + return; + } + + if (FAILED(hr = ID2D1GeometrySink_Close(sink))) + ERR("Failed to close geometry sink, hr %#lx.\n", hr); + ID2D1GeometrySink_Release(sink); + + transform = &render_target->drawing_state.transform; + prev_transform = *transform; + transform->_31 += baseline_origin.x * transform->_11 + baseline_origin.y * transform->_21; + transform->_32 += baseline_origin.x * transform->_12 + baseline_origin.y * transform->_22; + prev_antialias_mode = d2d_device_context_set_aa_mode_from_text_aa_mode(render_target); + d2d_device_context_fill_geometry(render_target, unsafe_impl_from_ID2D1Geometry((ID2D1Geometry *)geometry), + unsafe_impl_from_ID2D1Brush(brush), NULL); + render_target->drawing_state.antialiasMode = prev_antialias_mode; + *transform = prev_transform; + + ID2D1PathGeometry_Release(geometry); +} + +static void d2d_device_context_draw_glyph_run_bitmap(struct d2d_device_context *render_target, + D2D1_POINT_2F baseline_origin, const DWRITE_GLYPH_RUN *glyph_run, ID2D1Brush *brush, + DWRITE_RENDERING_MODE rendering_mode, DWRITE_MEASURING_MODE measuring_mode, + DWRITE_TEXT_ANTIALIAS_MODE antialias_mode) +{ + ID2D1RectangleGeometry *geometry = NULL; + ID2D1BitmapBrush *opacity_brush = NULL; + D2D1_BITMAP_PROPERTIES bitmap_desc; + ID2D1Bitmap *opacity_bitmap = NULL; + IDWriteGlyphRunAnalysis *analysis; + DWRITE_TEXTURE_TYPE texture_type; + D2D1_BRUSH_PROPERTIES brush_desc; + IDWriteFactory2 *dwrite_factory; + D2D1_MATRIX_3X2_F *transform, m; + void *opacity_values = NULL; + size_t opacity_values_size; + D2D1_SIZE_U bitmap_size; + float scale_x, scale_y; + D2D1_RECT_F run_rect; + RECT bounds; + HRESULT hr; + + if (FAILED(hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, + &IID_IDWriteFactory2, (IUnknown **)&dwrite_factory))) + { + ERR("Failed to create dwrite factory, hr %#lx.\n", hr); + return; + } + + transform = &render_target->drawing_state.transform; + + scale_x = render_target->desc.dpiX / 96.0f; + m._11 = transform->_11 * scale_x; + m._21 = transform->_21 * scale_x; + m._31 = transform->_31 * scale_x; + + scale_y = render_target->desc.dpiY / 96.0f; + m._12 = transform->_12 * scale_y; + m._22 = transform->_22 * scale_y; + m._32 = transform->_32 * scale_y; + + hr = IDWriteFactory2_CreateGlyphRunAnalysis(dwrite_factory, glyph_run, (DWRITE_MATRIX *)&m, + rendering_mode, measuring_mode, DWRITE_GRID_FIT_MODE_DEFAULT, antialias_mode, + baseline_origin.x, baseline_origin.y, &analysis); + IDWriteFactory2_Release(dwrite_factory); + if (FAILED(hr)) + { + ERR("Failed to create glyph run analysis, hr %#lx.\n", hr); + return; + } + + if (rendering_mode == DWRITE_RENDERING_MODE_ALIASED || antialias_mode == DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE) + texture_type = DWRITE_TEXTURE_ALIASED_1x1; + else + texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + + if (FAILED(hr = IDWriteGlyphRunAnalysis_GetAlphaTextureBounds(analysis, texture_type, &bounds))) + { + ERR("Failed to get alpha texture bounds, hr %#lx.\n", hr); + goto done; + } + + d2d_size_set(&bitmap_size, bounds.right - bounds.left, bounds.bottom - bounds.top); + if (!bitmap_size.width || !bitmap_size.height) + { + /* Empty run, nothing to do. */ + goto done; + } + + if (texture_type == DWRITE_TEXTURE_CLEARTYPE_3x1) + bitmap_size.width *= 3; + if (!(opacity_values = calloc(bitmap_size.height, bitmap_size.width))) + { + ERR("Failed to allocate opacity values.\n"); + goto done; + } + opacity_values_size = bitmap_size.height * bitmap_size.width; + + if (FAILED(hr = IDWriteGlyphRunAnalysis_CreateAlphaTexture(analysis, + texture_type, &bounds, opacity_values, opacity_values_size))) + { + ERR("Failed to create alpha texture, hr %#lx.\n", hr); + goto done; + } + + bitmap_desc.pixelFormat.format = DXGI_FORMAT_A8_UNORM; + bitmap_desc.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; + bitmap_desc.dpiX = render_target->desc.dpiX; + if (texture_type == DWRITE_TEXTURE_CLEARTYPE_3x1) + bitmap_desc.dpiX *= 3.0f; + bitmap_desc.dpiY = render_target->desc.dpiY; + if (FAILED(hr = d2d_device_context_CreateBitmap(&render_target->ID2D1DeviceContext1_iface, + bitmap_size, opacity_values, bitmap_size.width, &bitmap_desc, &opacity_bitmap))) + { + ERR("Failed to create opacity bitmap, hr %#lx.\n", hr); + goto done; + } + + d2d_rect_set(&run_rect, bounds.left / scale_x, bounds.top / scale_y, + bounds.right / scale_x, bounds.bottom / scale_y); + + brush_desc.opacity = 1.0f; + brush_desc.transform._11 = 1.0f; + brush_desc.transform._12 = 0.0f; + brush_desc.transform._21 = 0.0f; + brush_desc.transform._22 = 1.0f; + brush_desc.transform._31 = run_rect.left; + brush_desc.transform._32 = run_rect.top; + if (FAILED(hr = d2d_device_context_CreateBitmapBrush(&render_target->ID2D1DeviceContext1_iface, + opacity_bitmap, NULL, &brush_desc, &opacity_brush))) + { + ERR("Failed to create opacity bitmap brush, hr %#lx.\n", hr); + goto done; + } + + if (FAILED(hr = ID2D1Factory_CreateRectangleGeometry(render_target->factory, &run_rect, &geometry))) + { + ERR("Failed to create geometry, hr %#lx.\n", hr); + goto done; + } + + m = *transform; + *transform = identity; + d2d_device_context_fill_geometry(render_target, unsafe_impl_from_ID2D1Geometry((ID2D1Geometry *)geometry), + unsafe_impl_from_ID2D1Brush(brush), unsafe_impl_from_ID2D1Brush((ID2D1Brush *)opacity_brush)); + *transform = m; + +done: + if (geometry) + ID2D1RectangleGeometry_Release(geometry); + if (opacity_brush) + ID2D1BitmapBrush_Release(opacity_brush); + if (opacity_bitmap) + ID2D1Bitmap_Release(opacity_bitmap); + free(opacity_values); + IDWriteGlyphRunAnalysis_Release(analysis); +} + +static void d2d_device_context_draw_glyph_run(struct d2d_device_context *context, + D2D1_POINT_2F baseline_origin, const DWRITE_GLYPH_RUN *glyph_run, + const DWRITE_GLYPH_RUN_DESCRIPTION *glyph_run_desc, ID2D1Brush *brush, DWRITE_MEASURING_MODE measuring_mode) +{ + DWRITE_TEXT_ANTIALIAS_MODE antialias_mode = DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE; + IDWriteRenderingParams *rendering_params; + DWRITE_RENDERING_MODE rendering_mode; + HRESULT hr; + + if (FAILED(context->error.code)) + return; + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + d2d_command_list_draw_glyph_run(context->target.command_list, context, baseline_origin, glyph_run, + glyph_run_desc, brush, measuring_mode); + return; + } + + rendering_params = context->text_rendering_params ? context->text_rendering_params + : context->default_text_rendering_params; + + rendering_mode = IDWriteRenderingParams_GetRenderingMode(rendering_params); + + switch (context->drawing_state.textAntialiasMode) + { + case D2D1_TEXT_ANTIALIAS_MODE_ALIASED: + if (rendering_mode == DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL + || rendering_mode == DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC + || rendering_mode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL + || rendering_mode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC) + d2d_device_context_set_error(context, E_INVALIDARG); + break; + + case D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE: + if (rendering_mode == DWRITE_RENDERING_MODE_ALIASED + || rendering_mode == DWRITE_RENDERING_MODE_OUTLINE) + d2d_device_context_set_error(context, E_INVALIDARG); + break; + + case D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE: + if (rendering_mode == DWRITE_RENDERING_MODE_ALIASED) + d2d_device_context_set_error(context, E_INVALIDARG); + break; + + default: + break; + } + + if (FAILED(context->error.code)) + return; + + rendering_mode = DWRITE_RENDERING_MODE_DEFAULT; + switch (context->drawing_state.textAntialiasMode) + { + case D2D1_TEXT_ANTIALIAS_MODE_DEFAULT: + if (IDWriteRenderingParams_GetClearTypeLevel(rendering_params) > 0.0f) + antialias_mode = DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE; + break; + + case D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE: + antialias_mode = DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE; + break; + + case D2D1_TEXT_ANTIALIAS_MODE_ALIASED: + rendering_mode = DWRITE_RENDERING_MODE_ALIASED; + break; + + default: + break; + } + + if (rendering_mode == DWRITE_RENDERING_MODE_DEFAULT) + { + if (FAILED(hr = IDWriteFontFace_GetRecommendedRenderingMode(glyph_run->fontFace, glyph_run->fontEmSize, + max(context->desc.dpiX, context->desc.dpiY) / 96.0f, + measuring_mode, rendering_params, &rendering_mode))) + { + ERR("Failed to get recommended rendering mode, hr %#lx.\n", hr); + rendering_mode = DWRITE_RENDERING_MODE_OUTLINE; + } + } + + if (rendering_mode == DWRITE_RENDERING_MODE_OUTLINE) + d2d_device_context_draw_glyph_run_outline(context, baseline_origin, glyph_run, brush); + else + d2d_device_context_draw_glyph_run_bitmap(context, baseline_origin, glyph_run, brush, + rendering_mode, measuring_mode, antialias_mode); +} + +static void STDMETHODCALLTYPE d2d_device_context_DrawGlyphRun(ID2D1DeviceContext1 *iface, + D2D1_POINT_2F baseline_origin, const DWRITE_GLYPH_RUN *glyph_run, ID2D1Brush *brush, + DWRITE_MEASURING_MODE measuring_mode) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, baseline_origin %s, glyph_run %p, brush %p, measuring_mode %#x.\n", + iface, debug_d2d_point_2f(&baseline_origin), glyph_run, brush, measuring_mode); + + d2d_device_context_draw_glyph_run(context, baseline_origin, glyph_run, NULL, brush, measuring_mode); +} + +static void STDMETHODCALLTYPE d2d_device_context_SetTransform(ID2D1DeviceContext1 *iface, + const D2D1_MATRIX_3X2_F *transform) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, transform %p.\n", iface, transform); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_set_transform(context->target.command_list, transform); + + context->drawing_state.transform = *transform; +} + +static void STDMETHODCALLTYPE d2d_device_context_GetTransform(ID2D1DeviceContext1 *iface, + D2D1_MATRIX_3X2_F *transform) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, transform %p.\n", iface, transform); + + *transform = render_target->drawing_state.transform; +} + +static void STDMETHODCALLTYPE d2d_device_context_SetAntialiasMode(ID2D1DeviceContext1 *iface, + D2D1_ANTIALIAS_MODE antialias_mode) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, antialias_mode %#x stub!\n", iface, antialias_mode); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_set_antialias_mode(context->target.command_list, antialias_mode); + + context->drawing_state.antialiasMode = antialias_mode; +} + +static D2D1_ANTIALIAS_MODE STDMETHODCALLTYPE d2d_device_context_GetAntialiasMode(ID2D1DeviceContext1 *iface) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p.\n", iface); + + return render_target->drawing_state.antialiasMode; +} + +static void STDMETHODCALLTYPE d2d_device_context_SetTextAntialiasMode(ID2D1DeviceContext1 *iface, + D2D1_TEXT_ANTIALIAS_MODE antialias_mode) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, antialias_mode %#x.\n", iface, antialias_mode); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_set_text_antialias_mode(context->target.command_list, antialias_mode); + + context->drawing_state.textAntialiasMode = antialias_mode; +} + +static D2D1_TEXT_ANTIALIAS_MODE STDMETHODCALLTYPE d2d_device_context_GetTextAntialiasMode(ID2D1DeviceContext1 *iface) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p.\n", iface); + + return render_target->drawing_state.textAntialiasMode; +} + +static void STDMETHODCALLTYPE d2d_device_context_SetTextRenderingParams(ID2D1DeviceContext1 *iface, + IDWriteRenderingParams *text_rendering_params) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, text_rendering_params %p.\n", iface, text_rendering_params); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_set_text_rendering_params(context->target.command_list, text_rendering_params); + + if (text_rendering_params) + IDWriteRenderingParams_AddRef(text_rendering_params); + if (context->text_rendering_params) + IDWriteRenderingParams_Release(context->text_rendering_params); + context->text_rendering_params = text_rendering_params; +} + +static void STDMETHODCALLTYPE d2d_device_context_GetTextRenderingParams(ID2D1DeviceContext1 *iface, + IDWriteRenderingParams **text_rendering_params) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, text_rendering_params %p.\n", iface, text_rendering_params); + + if ((*text_rendering_params = render_target->text_rendering_params)) + IDWriteRenderingParams_AddRef(*text_rendering_params); +} + +static void STDMETHODCALLTYPE d2d_device_context_SetTags(ID2D1DeviceContext1 *iface, D2D1_TAG tag1, D2D1_TAG tag2) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, tag1 %s, tag2 %s.\n", iface, wine_dbgstr_longlong(tag1), wine_dbgstr_longlong(tag2)); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_set_tags(context->target.command_list, tag1, tag2); + + context->drawing_state.tag1 = tag1; + context->drawing_state.tag2 = tag2; +} + +static void STDMETHODCALLTYPE d2d_device_context_GetTags(ID2D1DeviceContext1 *iface, D2D1_TAG *tag1, D2D1_TAG *tag2) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, tag1 %p, tag2 %p.\n", iface, tag1, tag2); + + *tag1 = render_target->drawing_state.tag1; + *tag2 = render_target->drawing_state.tag2; +} + +static void STDMETHODCALLTYPE d2d_device_context_PushLayer(ID2D1DeviceContext1 *iface, + const D2D1_LAYER_PARAMETERS *layer_parameters, ID2D1Layer *layer) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + FIXME("iface %p, layer_parameters %p, layer %p stub!\n", iface, layer_parameters, layer); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + D2D1_LAYER_PARAMETERS1 parameters; + + memcpy(¶meters, layer_parameters, sizeof(*layer_parameters)); + parameters.layerOptions = D2D1_LAYER_OPTIONS1_NONE; + d2d_command_list_push_layer(context->target.command_list, context, ¶meters, layer); + } +} + +static void STDMETHODCALLTYPE d2d_device_context_PopLayer(ID2D1DeviceContext1 *iface) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + FIXME("iface %p stub!\n", iface); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_pop_layer(context->target.command_list); +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_Flush(ID2D1DeviceContext1 *iface, D2D1_TAG *tag1, D2D1_TAG *tag2) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + FIXME("iface %p, tag1 %p, tag2 %p stub!\n", iface, tag1, tag2); + + if (context->ops && context->ops->device_context_present) + context->ops->device_context_present(context->outer_unknown); + + return E_NOTIMPL; +} + +static void STDMETHODCALLTYPE d2d_device_context_SaveDrawingState(ID2D1DeviceContext1 *iface, + ID2D1DrawingStateBlock *state_block) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + struct d2d_state_block *state_block_impl; + + TRACE("iface %p, state_block %p.\n", iface, state_block); + + if (!(state_block_impl = unsafe_impl_from_ID2D1DrawingStateBlock(state_block))) return; + state_block_impl->drawing_state = render_target->drawing_state; + if (render_target->text_rendering_params) + IDWriteRenderingParams_AddRef(render_target->text_rendering_params); + if (state_block_impl->text_rendering_params) + IDWriteRenderingParams_Release(state_block_impl->text_rendering_params); + state_block_impl->text_rendering_params = render_target->text_rendering_params; +} + +static void STDMETHODCALLTYPE d2d_device_context_RestoreDrawingState(ID2D1DeviceContext1 *iface, + ID2D1DrawingStateBlock *state_block) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_state_block *state_block_impl; + + TRACE("iface %p, state_block %p.\n", iface, state_block); + + if (!(state_block_impl = unsafe_impl_from_ID2D1DrawingStateBlock(state_block))) return; + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + struct d2d_command_list *command_list = context->target.command_list; + + if (context->drawing_state.antialiasMode != state_block_impl->drawing_state.antialiasMode) + d2d_command_list_set_antialias_mode(command_list, state_block_impl->drawing_state.antialiasMode); + d2d_command_list_set_text_antialias_mode(command_list, state_block_impl->drawing_state.textAntialiasMode); + d2d_command_list_set_tags(command_list, state_block_impl->drawing_state.tag1, state_block_impl->drawing_state.tag2); + d2d_command_list_set_transform(command_list, &state_block_impl->drawing_state.transform); + d2d_command_list_set_primitive_blend(command_list, state_block_impl->drawing_state.primitiveBlend); + d2d_command_list_set_unit_mode(command_list, state_block_impl->drawing_state.unitMode); + d2d_command_list_set_text_rendering_params(command_list, state_block_impl->text_rendering_params); + } + + context->drawing_state = state_block_impl->drawing_state; + if (state_block_impl->text_rendering_params) + IDWriteRenderingParams_AddRef(state_block_impl->text_rendering_params); + if (context->text_rendering_params) + IDWriteRenderingParams_Release(context->text_rendering_params); + context->text_rendering_params = state_block_impl->text_rendering_params; +} + +static void STDMETHODCALLTYPE d2d_device_context_PushAxisAlignedClip(ID2D1DeviceContext1 *iface, + const D2D1_RECT_F *clip_rect, D2D1_ANTIALIAS_MODE antialias_mode) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + D2D1_RECT_F transformed_rect; + float x_scale, y_scale; + D2D1_POINT_2F point; + + TRACE("iface %p, clip_rect %s, antialias_mode %#x.\n", iface, debug_d2d_rect_f(clip_rect), antialias_mode); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_push_clip(context->target.command_list, clip_rect, antialias_mode); + + if (antialias_mode != D2D1_ANTIALIAS_MODE_ALIASED) + FIXME("Ignoring antialias_mode %#x.\n", antialias_mode); + + x_scale = context->desc.dpiX / 96.0f; + y_scale = context->desc.dpiY / 96.0f; + d2d_point_transform(&point, &context->drawing_state.transform, + clip_rect->left * x_scale, clip_rect->top * y_scale); + d2d_rect_set(&transformed_rect, point.x, point.y, point.x, point.y); + d2d_point_transform(&point, &context->drawing_state.transform, + clip_rect->left * x_scale, clip_rect->bottom * y_scale); + d2d_rect_expand(&transformed_rect, &point); + d2d_point_transform(&point, &context->drawing_state.transform, + clip_rect->right * x_scale, clip_rect->top * y_scale); + d2d_rect_expand(&transformed_rect, &point); + d2d_point_transform(&point, &context->drawing_state.transform, + clip_rect->right * x_scale, clip_rect->bottom * y_scale); + d2d_rect_expand(&transformed_rect, &point); + + if (!d2d_clip_stack_push(&context->clip_stack, &transformed_rect)) + WARN("Failed to push clip rect.\n"); +} + +static void STDMETHODCALLTYPE d2d_device_context_PopAxisAlignedClip(ID2D1DeviceContext1 *iface) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p.\n", iface); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_pop_clip(context->target.command_list); + + d2d_clip_stack_pop(&context->clip_stack); +} + +static void STDMETHODCALLTYPE d2d_device_context_Clear(ID2D1DeviceContext1 *iface, const D2D1_COLOR_F *colour) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + D3D11_MAPPED_SUBRESOURCE map_desc; + ID3D11DeviceContext *d3d_context; + struct d2d_ps_cb *ps_cb_data; + struct d2d_vs_cb *vs_cb_data; + D2D1_COLOR_F *c; + HRESULT hr; + + TRACE("iface %p, colour %p.\n", iface, colour); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + d2d_command_list_clear(context->target.command_list, colour); + return; + } + + ID3D11Device1_GetImmediateContext(context->d3d_device, &d3d_context); + + if (FAILED(hr = ID3D11DeviceContext_Map(d3d_context, (ID3D11Resource *)context->vs_cb, + 0, D3D11_MAP_WRITE_DISCARD, 0, &map_desc))) + { + WARN("Failed to map vs constant buffer, hr %#lx.\n", hr); + ID3D11DeviceContext_Release(d3d_context); + return; + } + + vs_cb_data = map_desc.pData; + vs_cb_data->transform_geometry._11 = 1.0f; + vs_cb_data->transform_geometry._21 = 0.0f; + vs_cb_data->transform_geometry._31 = 0.0f; + vs_cb_data->transform_geometry.pad0 = 0.0f; + vs_cb_data->transform_geometry._12 = 0.0f; + vs_cb_data->transform_geometry._22 = 1.0f; + vs_cb_data->transform_geometry._32 = 0.0f; + vs_cb_data->transform_geometry.stroke_width = 0.0f; + vs_cb_data->transform_rtx.x = 1.0f; + vs_cb_data->transform_rtx.y = 0.0f; + vs_cb_data->transform_rtx.z = 1.0f; + vs_cb_data->transform_rtx.w = 1.0f; + vs_cb_data->transform_rty.x = 0.0f; + vs_cb_data->transform_rty.y = 1.0f; + vs_cb_data->transform_rty.z = 1.0f; + vs_cb_data->transform_rty.w = -1.0f; + + ID3D11DeviceContext_Unmap(d3d_context, (ID3D11Resource *)context->vs_cb, 0); + + if (FAILED(hr = ID3D11DeviceContext_Map(d3d_context, (ID3D11Resource *)context->ps_cb, + 0, D3D11_MAP_WRITE_DISCARD, 0, &map_desc))) + { + WARN("Failed to map ps constant buffer, hr %#lx.\n", hr); + ID3D11DeviceContext_Release(d3d_context); + return; + } + + ps_cb_data = map_desc.pData; + memset(ps_cb_data, 0, sizeof(*ps_cb_data)); + ps_cb_data->colour_brush.type = D2D_BRUSH_TYPE_SOLID; + ps_cb_data->colour_brush.opacity = 1.0f; + ps_cb_data->opacity_brush.type = D2D_BRUSH_TYPE_COUNT; + c = &ps_cb_data->colour_brush.u.solid.colour; + if (colour) + *c = *colour; + if (context->desc.pixelFormat.alphaMode == D2D1_ALPHA_MODE_IGNORE) + c->a = 1.0f; + c->r *= c->a; + c->g *= c->a; + c->b *= c->a; + + ID3D11DeviceContext_Unmap(d3d_context, (ID3D11Resource *)context->ps_cb, 0); + ID3D11DeviceContext_Release(d3d_context); + + d2d_device_context_draw(context, D2D_SHAPE_TYPE_TRIANGLE, context->ib, 6, + context->vb, context->vb_stride, NULL, NULL); +} + +static void STDMETHODCALLTYPE d2d_device_context_BeginDraw(ID2D1DeviceContext1 *iface) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p.\n", iface); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_begin_draw(context->target.command_list, context); + + memset(&context->error, 0, sizeof(context->error)); +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_EndDraw(ID2D1DeviceContext1 *iface, + D2D1_TAG *tag1, D2D1_TAG *tag2) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + HRESULT hr; + + TRACE("iface %p, tag1 %p, tag2 %p.\n", iface, tag1, tag2); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + FIXME("Unimplemented for command list target.\n"); + return E_NOTIMPL; + } + + if (tag1) + *tag1 = context->error.tag1; + if (tag2) + *tag2 = context->error.tag2; + + if (context->ops && context->ops->device_context_present) + { + if (FAILED(hr = context->ops->device_context_present(context->outer_unknown))) + context->error.code = hr; + } + + return context->error.code; +} + +static D2D1_PIXEL_FORMAT * STDMETHODCALLTYPE d2d_device_context_GetPixelFormat(ID2D1DeviceContext1 *iface, + D2D1_PIXEL_FORMAT *format) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, format %p.\n", iface, format); + + *format = render_target->desc.pixelFormat; + return format; +} + +static void STDMETHODCALLTYPE d2d_device_context_SetDpi(ID2D1DeviceContext1 *iface, float dpi_x, float dpi_y) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, dpi_x %.8e, dpi_y %.8e.\n", iface, dpi_x, dpi_y); + + if (dpi_x == 0.0f && dpi_y == 0.0f) + { + dpi_x = 96.0f; + dpi_y = 96.0f; + } + else if (dpi_x <= 0.0f || dpi_y <= 0.0f) + return; + + render_target->desc.dpiX = dpi_x; + render_target->desc.dpiY = dpi_y; +} + +static void STDMETHODCALLTYPE d2d_device_context_GetDpi(ID2D1DeviceContext1 *iface, float *dpi_x, float *dpi_y) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, dpi_x %p, dpi_y %p.\n", iface, dpi_x, dpi_y); + + *dpi_x = render_target->desc.dpiX; + *dpi_y = render_target->desc.dpiY; +} + +static D2D1_SIZE_F * STDMETHODCALLTYPE d2d_device_context_GetSize(ID2D1DeviceContext1 *iface, D2D1_SIZE_F *size) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, size %p.\n", iface, size); + + size->width = render_target->pixel_size.width / (render_target->desc.dpiX / 96.0f); + size->height = render_target->pixel_size.height / (render_target->desc.dpiY / 96.0f); + return size; +} + +static D2D1_SIZE_U * STDMETHODCALLTYPE d2d_device_context_GetPixelSize(ID2D1DeviceContext1 *iface, + D2D1_SIZE_U *pixel_size) +{ + struct d2d_device_context *render_target = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, pixel_size %p.\n", iface, pixel_size); + + *pixel_size = render_target->pixel_size; + return pixel_size; +} + +static UINT32 STDMETHODCALLTYPE d2d_device_context_GetMaximumBitmapSize(ID2D1DeviceContext1 *iface) +{ + TRACE("iface %p.\n", iface); + + return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; +} + +static BOOL STDMETHODCALLTYPE d2d_device_context_IsSupported(ID2D1DeviceContext1 *iface, + const D2D1_RENDER_TARGET_PROPERTIES *desc) +{ + FIXME("iface %p, desc %p stub!\n", iface, desc); + + return FALSE; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_ID2D1DeviceContext_CreateBitmap(ID2D1DeviceContext1 *iface, + D2D1_SIZE_U size, const void *src_data, UINT32 pitch, + const D2D1_BITMAP_PROPERTIES1 *desc, ID2D1Bitmap1 **bitmap) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_bitmap *object; + HRESULT hr; + + TRACE("iface %p, size {%u, %u}, src_data %p, pitch %u, desc %p, bitmap %p.\n", + iface, size.width, size.height, src_data, pitch, desc, bitmap); + + if (SUCCEEDED(hr = d2d_bitmap_create(context, size, src_data, pitch, desc, &object))) + *bitmap = &object->ID2D1Bitmap1_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_ID2D1DeviceContext_CreateBitmapFromWicBitmap( + ID2D1DeviceContext1 *iface, IWICBitmapSource *bitmap_source, + const D2D1_BITMAP_PROPERTIES1 *desc, ID2D1Bitmap1 **bitmap) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_bitmap *object; + HRESULT hr; + + TRACE("iface %p, bitmap_source %p, desc %p, bitmap %p.\n", iface, bitmap_source, desc, bitmap); + + if (SUCCEEDED(hr = d2d_bitmap_create_from_wic_bitmap(context, bitmap_source, desc, &object))) + *bitmap = &object->ID2D1Bitmap1_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateColorContext(ID2D1DeviceContext1 *iface, + D2D1_COLOR_SPACE space, const BYTE *profile, UINT32 profile_size, ID2D1ColorContext **color_context) +{ + FIXME("iface %p, space %#x, profile %p, profile_size %u, color_context %p stub!\n", + iface, space, profile, profile_size, color_context); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateColorContextFromFilename(ID2D1DeviceContext1 *iface, + const WCHAR *filename, ID2D1ColorContext **color_context) +{ + FIXME("iface %p, filename %s, color_context %p stub!\n", iface, debugstr_w(filename), color_context); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateColorContextFromWicColorContext(ID2D1DeviceContext1 *iface, + IWICColorContext *wic_color_context, ID2D1ColorContext **color_context) +{ + FIXME("iface %p, wic_color_context %p, color_context %p stub!\n", iface, wic_color_context, color_context); + + return E_NOTIMPL; +} + +static BOOL d2d_bitmap_check_options_with_surface(unsigned int options, unsigned int surface_options) +{ + switch (options) + { + case D2D1_BITMAP_OPTIONS_NONE: + case D2D1_BITMAP_OPTIONS_TARGET: + case D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW: + case D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW | D2D1_BITMAP_OPTIONS_GDI_COMPATIBLE: + case D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_GDI_COMPATIBLE: + case D2D1_BITMAP_OPTIONS_CANNOT_DRAW | D2D1_BITMAP_OPTIONS_CPU_READ: + case D2D1_BITMAP_OPTIONS_CANNOT_DRAW: + break; + default: + WARN("Invalid bitmap options %#x.\n", options); + return FALSE; + } + + if (options && (options & D2D1_BITMAP_OPTIONS_TARGET) != (surface_options & D2D1_BITMAP_OPTIONS_TARGET)) + return FALSE; + if (!(options & D2D1_BITMAP_OPTIONS_CANNOT_DRAW) && (surface_options & D2D1_BITMAP_OPTIONS_CANNOT_DRAW)) + return FALSE; + if (options & D2D1_BITMAP_OPTIONS_TARGET) + { + if (options & D2D1_BITMAP_OPTIONS_GDI_COMPATIBLE && !(surface_options & D2D1_BITMAP_OPTIONS_GDI_COMPATIBLE)) + return FALSE; + return TRUE; + } + + if (options & D2D1_BITMAP_OPTIONS_CANNOT_DRAW) + { + if (!(surface_options & D2D1_BITMAP_OPTIONS_CANNOT_DRAW)) + return FALSE; + + if (options & D2D1_BITMAP_OPTIONS_CPU_READ && !(surface_options & D2D1_BITMAP_OPTIONS_CPU_READ)) + return FALSE; + } + + return TRUE; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateBitmapFromDxgiSurface(ID2D1DeviceContext1 *iface, + IDXGISurface *surface, const D2D1_BITMAP_PROPERTIES1 *desc, ID2D1Bitmap1 **bitmap) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + D2D1_BITMAP_PROPERTIES1 bitmap_desc; + unsigned int surface_options; + struct d2d_bitmap *object; + HRESULT hr; + + TRACE("iface %p, surface %p, desc %p, bitmap %p.\n", iface, surface, desc, bitmap); + + surface_options = d2d_get_bitmap_options_for_surface(surface); + + if (desc) + { + if (!d2d_bitmap_check_options_with_surface(desc->bitmapOptions, surface_options)) + { + WARN("Incompatible bitmap options %#x, surface options %#x.\n", + desc->bitmapOptions, surface_options); + return E_INVALIDARG; + } + } + else + { + DXGI_SURFACE_DESC surface_desc; + + if (FAILED(hr = IDXGISurface_GetDesc(surface, &surface_desc))) + { + WARN("Failed to get surface desc, hr %#lx.\n", hr); + return hr; + } + + memset(&bitmap_desc, 0, sizeof(bitmap_desc)); + bitmap_desc.pixelFormat.format = surface_desc.Format; + bitmap_desc.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; + bitmap_desc.bitmapOptions = surface_options; + desc = &bitmap_desc; + } + + if (SUCCEEDED(hr = d2d_bitmap_create_shared(context, &IID_IDXGISurface, surface, desc, &object))) + *bitmap = &object->ID2D1Bitmap1_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateEffect(ID2D1DeviceContext1 *iface, + REFCLSID effect_id, ID2D1Effect **effect) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, effect_id %s, effect %p.\n", iface, debugstr_guid(effect_id), effect); + + return d2d_effect_create(context, effect_id, effect); +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_ID2D1DeviceContext_CreateGradientStopCollection( + ID2D1DeviceContext1 *iface, const D2D1_GRADIENT_STOP *stops, UINT32 stop_count, + D2D1_COLOR_SPACE preinterpolation_space, D2D1_COLOR_SPACE postinterpolation_space, + D2D1_BUFFER_PRECISION buffer_precision, D2D1_EXTEND_MODE extend_mode, + D2D1_COLOR_INTERPOLATION_MODE color_interpolation_mode, ID2D1GradientStopCollection1 **gradient) +{ + FIXME("iface %p, stops %p, stop_count %u, preinterpolation_space %#x, postinterpolation_space %#x, " + "buffer_precision %#x, extend_mode %#x, color_interpolation_mode %#x, gradient %p stub!\n", + iface, stops, stop_count, preinterpolation_space, postinterpolation_space, + buffer_precision, extend_mode, color_interpolation_mode, gradient); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateImageBrush(ID2D1DeviceContext1 *iface, + ID2D1Image *image, const D2D1_IMAGE_BRUSH_PROPERTIES *image_brush_desc, + const D2D1_BRUSH_PROPERTIES *brush_desc, ID2D1ImageBrush **brush) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_brush *object; + HRESULT hr; + + TRACE("iface %p, image %p, image_brush_desc %p, brush_desc %p, brush %p.\n", iface, image, image_brush_desc, + brush_desc, brush); + + if (SUCCEEDED(hr = d2d_image_brush_create(context->factory, image, image_brush_desc, + brush_desc, &object))) + *brush = (ID2D1ImageBrush *)&object->ID2D1Brush_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_ID2D1DeviceContext_CreateBitmapBrush(ID2D1DeviceContext1 *iface, + ID2D1Bitmap *bitmap, const D2D1_BITMAP_BRUSH_PROPERTIES1 *bitmap_brush_desc, + const D2D1_BRUSH_PROPERTIES *brush_desc, ID2D1BitmapBrush1 **brush) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_brush *object; + HRESULT hr; + + TRACE("iface %p, bitmap %p, bitmap_brush_desc %p, brush_desc %p, brush %p.\n", iface, bitmap, bitmap_brush_desc, + brush_desc, brush); + + if (SUCCEEDED(hr = d2d_bitmap_brush_create(context->factory, bitmap, bitmap_brush_desc, brush_desc, &object))) + *brush = (ID2D1BitmapBrush1 *)&object->ID2D1Brush_iface; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateCommandList(ID2D1DeviceContext1 *iface, + ID2D1CommandList **command_list) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_command_list *object; + HRESULT hr; + + TRACE("iface %p, command_list %p.\n", iface, command_list); + + if (SUCCEEDED(hr = d2d_command_list_create(context->factory, &object))) + *command_list = &object->ID2D1CommandList_iface; + + return hr; +} + +static BOOL STDMETHODCALLTYPE d2d_device_context_IsDxgiFormatSupported(ID2D1DeviceContext1 *iface, DXGI_FORMAT format) +{ + FIXME("iface %p, format %#x stub!\n", iface, format); + + return FALSE; +} + +static BOOL STDMETHODCALLTYPE d2d_device_context_IsBufferPrecisionSupported(ID2D1DeviceContext1 *iface, + D2D1_BUFFER_PRECISION buffer_precision) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + DXGI_FORMAT format; + UINT support = 0; + HRESULT hr; + + TRACE("iface %p, buffer_precision %u.\n", iface, buffer_precision); + + switch (buffer_precision) + { + case D2D1_BUFFER_PRECISION_8BPC_UNORM: format = DXGI_FORMAT_R8G8B8A8_UNORM; break; + case D2D1_BUFFER_PRECISION_8BPC_UNORM_SRGB: format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; break; + case D2D1_BUFFER_PRECISION_16BPC_UNORM: format = DXGI_FORMAT_R16G16B16A16_UNORM; break; + case D2D1_BUFFER_PRECISION_16BPC_FLOAT: format = DXGI_FORMAT_R16G16B16A16_FLOAT; break; + case D2D1_BUFFER_PRECISION_32BPC_FLOAT: format = DXGI_FORMAT_R32G32B32A32_FLOAT; break; + default: + WARN("Unexpected precision %u.\n", buffer_precision); + return FALSE; + } + + if (FAILED(hr = ID3D11Device1_CheckFormatSupport(context->d3d_device, format, &support))) + { + WARN("Format support check failed, hr %#lx.\n", hr); + } + + return !!(support & D3D11_FORMAT_SUPPORT_BUFFER); +} + +static void STDMETHODCALLTYPE d2d_device_context_GetImageLocalBounds(ID2D1DeviceContext1 *iface, + ID2D1Image *image, D2D1_RECT_F *local_bounds) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + D2D_SIZE_U pixel_size; + ID2D1Bitmap *bitmap; + D2D_SIZE_F size; + + TRACE("iface %p, image %p, local_bounds %p.\n", iface, image, local_bounds); + + if (SUCCEEDED(ID2D1Image_QueryInterface(image, &IID_ID2D1Bitmap, (void **)&bitmap))) + { + local_bounds->left = 0.0f; + local_bounds->top = 0.0f; + switch (context->drawing_state.unitMode) + { + case D2D1_UNIT_MODE_DIPS: + size = ID2D1Bitmap_GetSize(bitmap); + local_bounds->right = size.width; + local_bounds->bottom = size.height; + break; + + case D2D1_UNIT_MODE_PIXELS: + pixel_size = ID2D1Bitmap_GetPixelSize(bitmap); + local_bounds->right = pixel_size.width; + local_bounds->bottom = pixel_size.height; + break; + + default: + WARN("Unknown unit mode %#x.\n", context->drawing_state.unitMode); + break; + } + ID2D1Bitmap_Release(bitmap); + } + else + { + FIXME("Unable to get local bounds of image %p.\n", image); + } +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_GetImageWorldBounds(ID2D1DeviceContext1 *iface, + ID2D1Image *image, D2D1_RECT_F *world_bounds) +{ + FIXME("iface %p, image %p, world_bounds %p stub!\n", iface, image, world_bounds); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_GetGlyphRunWorldBounds(ID2D1DeviceContext1 *iface, + D2D1_POINT_2F baseline_origin, const DWRITE_GLYPH_RUN *glyph_run, + DWRITE_MEASURING_MODE measuring_mode, D2D1_RECT_F *bounds) +{ + FIXME("iface %p, baseline_origin %s, glyph_run %p, measuring_mode %#x, bounds %p stub!\n", + iface, debug_d2d_point_2f(&baseline_origin), glyph_run, measuring_mode, bounds); + + return E_NOTIMPL; +} + +static void STDMETHODCALLTYPE d2d_device_context_GetDevice(ID2D1DeviceContext1 *iface, ID2D1Device **device) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, device %p.\n", iface, device); + + *device = (ID2D1Device *)&context->device->ID2D1Device1_iface; + ID2D1Device_AddRef(*device); +} + +static void d2d_device_context_reset_target(struct d2d_device_context *context) +{ + if (!context->target.object) + return; + + IUnknown_Release(context->target.object); + memset(&context->target, 0, sizeof(context->target)); + + /* Note that DPI settings are kept. */ + memset(&context->desc.pixelFormat, 0, sizeof(context->desc.pixelFormat)); + memset(&context->pixel_size, 0, sizeof(context->pixel_size)); + + if (context->bs) + ID3D11BlendState_Release(context->bs); + context->bs = NULL; +} + +static void STDMETHODCALLTYPE d2d_device_context_SetTarget(ID2D1DeviceContext1 *iface, ID2D1Image *target) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + struct d2d_command_list *command_list_impl; + struct d2d_bitmap *bitmap_impl; + ID2D1CommandList *command_list; + D3D11_BLEND_DESC blend_desc; + ID2D1Bitmap *bitmap; + HRESULT hr; + + TRACE("iface %p, target %p.\n", iface, target); + + if (!target) + { + d2d_device_context_reset_target(context); + return; + } + + if (SUCCEEDED(ID2D1Image_QueryInterface(target, &IID_ID2D1Bitmap, (void **)&bitmap))) + { + bitmap_impl = unsafe_impl_from_ID2D1Bitmap(bitmap); + + if (!(bitmap_impl->options & D2D1_BITMAP_OPTIONS_TARGET)) + { + ID2D1Bitmap_Release(bitmap); + d2d_device_context_set_error(context, D2DERR_INVALID_TARGET); + return; + } + + d2d_device_context_reset_target(context); + + /* Set sizes and pixel format. */ + context->pixel_size = bitmap_impl->pixel_size; + context->desc.pixelFormat = bitmap_impl->format; + context->target.bitmap = bitmap_impl; + context->target.object = target; + context->target.type = D2D_TARGET_BITMAP; + + memset(&blend_desc, 0, sizeof(blend_desc)); + blend_desc.IndependentBlendEnable = FALSE; + blend_desc.RenderTarget[0].BlendEnable = TRUE; + blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + if (FAILED(hr = ID3D11Device1_CreateBlendState(context->d3d_device, &blend_desc, &context->bs))) + WARN("Failed to create blend state, hr %#lx.\n", hr); + } + else if (SUCCEEDED(ID2D1Image_QueryInterface(target, &IID_ID2D1CommandList, (void **)&command_list))) + { + command_list_impl = unsafe_impl_from_ID2D1CommandList(command_list); + + d2d_device_context_reset_target(context); + + context->target.command_list = command_list_impl; + context->target.object = target; + context->target.type = D2D_TARGET_COMMAND_LIST; + } + else + { + WARN("Unsupported target type.\n"); + } +} + +static void STDMETHODCALLTYPE d2d_device_context_GetTarget(ID2D1DeviceContext1 *iface, ID2D1Image **target) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, target %p.\n", iface, target); + + *target = context->target.object ? context->target.object : NULL; + if (*target) + ID2D1Image_AddRef(*target); +} + +static void STDMETHODCALLTYPE d2d_device_context_SetRenderingControls(ID2D1DeviceContext1 *iface, + const D2D1_RENDERING_CONTROLS *rendering_controls) +{ + FIXME("iface %p, rendering_controls %p stub!\n", iface, rendering_controls); +} + +static void STDMETHODCALLTYPE d2d_device_context_GetRenderingControls(ID2D1DeviceContext1 *iface, + D2D1_RENDERING_CONTROLS *rendering_controls) +{ + FIXME("iface %p, rendering_controls %p stub!\n", iface, rendering_controls); +} + +static void STDMETHODCALLTYPE d2d_device_context_SetPrimitiveBlend(ID2D1DeviceContext1 *iface, + D2D1_PRIMITIVE_BLEND primitive_blend) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, primitive_blend %u.\n", iface, primitive_blend); + + if (primitive_blend > D2D1_PRIMITIVE_BLEND_MAX) + { + WARN("Unknown blend mode %u.\n", primitive_blend); + return; + } + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_set_primitive_blend(context->target.command_list, primitive_blend); + + context->drawing_state.primitiveBlend = primitive_blend; +} + +static D2D1_PRIMITIVE_BLEND STDMETHODCALLTYPE d2d_device_context_GetPrimitiveBlend(ID2D1DeviceContext1 *iface) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p.\n", iface); + + return context->drawing_state.primitiveBlend; +} + +static void STDMETHODCALLTYPE d2d_device_context_SetUnitMode(ID2D1DeviceContext1 *iface, D2D1_UNIT_MODE unit_mode) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, unit_mode %#x.\n", iface, unit_mode); + + if (unit_mode != D2D1_UNIT_MODE_DIPS && unit_mode != D2D1_UNIT_MODE_PIXELS) + { + WARN("Unknown unit mode %#x.\n", unit_mode); + return; + } + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_set_unit_mode(context->target.command_list, unit_mode); + + context->drawing_state.unitMode = unit_mode; +} + +static D2D1_UNIT_MODE STDMETHODCALLTYPE d2d_device_context_GetUnitMode(ID2D1DeviceContext1 *iface) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p.\n", iface); + + return context->drawing_state.unitMode; +} + +static void STDMETHODCALLTYPE d2d_device_context_ID2D1DeviceContext_DrawGlyphRun(ID2D1DeviceContext1 *iface, + D2D1_POINT_2F baseline_origin, const DWRITE_GLYPH_RUN *glyph_run, + const DWRITE_GLYPH_RUN_DESCRIPTION *glyph_run_desc, ID2D1Brush *brush, DWRITE_MEASURING_MODE measuring_mode) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, baseline_origin %s, glyph_run %p, glyph_run_desc %p, brush %p, measuring_mode %#x.\n", + iface, debug_d2d_point_2f(&baseline_origin), glyph_run, glyph_run_desc, brush, measuring_mode); + + d2d_device_context_draw_glyph_run(context, baseline_origin, glyph_run, glyph_run_desc, brush, measuring_mode); +} + +static void STDMETHODCALLTYPE d2d_device_context_DrawImage(ID2D1DeviceContext1 *iface, ID2D1Image *image, + const D2D1_POINT_2F *target_offset, const D2D1_RECT_F *image_rect, D2D1_INTERPOLATION_MODE interpolation_mode, + D2D1_COMPOSITE_MODE composite_mode) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + ID2D1Bitmap *bitmap; + + TRACE("iface %p, image %p, target_offset %s, image_rect %s, interpolation_mode %#x, composite_mode %#x.\n", + iface, image, debug_d2d_point_2f(target_offset), debug_d2d_rect_f(image_rect), + interpolation_mode, composite_mode); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + d2d_command_list_draw_image(context->target.command_list, image, target_offset, image_rect, + interpolation_mode, composite_mode); + return; + } + + if (composite_mode != D2D1_COMPOSITE_MODE_SOURCE_OVER) + FIXME("Unhandled composite mode %#x.\n", composite_mode); + + if (SUCCEEDED(ID2D1Image_QueryInterface(image, &IID_ID2D1Bitmap, (void **)&bitmap))) + { + d2d_device_context_draw_bitmap(context, bitmap, NULL, 1.0f, d2d1_1_interp_mode_from_d2d1(interpolation_mode), + image_rect, target_offset, NULL); + + ID2D1Bitmap_Release(bitmap); + return; + } + + FIXME("Unhandled image %p.\n", image); +} + +static void STDMETHODCALLTYPE d2d_device_context_DrawGdiMetafile(ID2D1DeviceContext1 *iface, + ID2D1GdiMetafile *metafile, const D2D1_POINT_2F *target_offset) +{ + FIXME("iface %p, metafile %p, target_offset %s stub!\n", + iface, metafile, debug_d2d_point_2f(target_offset)); +} + +static void STDMETHODCALLTYPE d2d_device_context_ID2D1DeviceContext_DrawBitmap(ID2D1DeviceContext1 *iface, + ID2D1Bitmap *bitmap, const D2D1_RECT_F *dst_rect, float opacity, D2D1_INTERPOLATION_MODE interpolation_mode, + const D2D1_RECT_F *src_rect, const D2D1_MATRIX_4X4_F *perspective_transform) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + TRACE("iface %p, bitmap %p, dst_rect %s, opacity %.8e, interpolation_mode %#x, " + "src_rect %s, perspective_transform %p.\n", + iface, bitmap, debug_d2d_rect_f(dst_rect), opacity, interpolation_mode, + debug_d2d_rect_f(src_rect), perspective_transform); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + { + d2d_command_list_draw_bitmap(context->target.command_list, bitmap, dst_rect, opacity, interpolation_mode, + src_rect, perspective_transform); + } + else + { + d2d_device_context_draw_bitmap(context, bitmap, dst_rect, opacity, interpolation_mode, src_rect, + NULL, perspective_transform); + } +} + +static void STDMETHODCALLTYPE d2d_device_context_ID2D1DeviceContext_PushLayer(ID2D1DeviceContext1 *iface, + const D2D1_LAYER_PARAMETERS1 *layer_parameters, ID2D1Layer *layer) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + FIXME("iface %p, layer_parameters %p, layer %p stub!\n", iface, layer_parameters, layer); + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_push_layer(context->target.command_list, context, layer_parameters, layer); +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_InvalidateEffectInputRectangle(ID2D1DeviceContext1 *iface, + ID2D1Effect *effect, UINT32 input, const D2D1_RECT_F *input_rect) +{ + FIXME("iface %p, effect %p, input %u, input_rect %s stub!\n", + iface, effect, input, debug_d2d_rect_f(input_rect)); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_GetEffectInvalidRectangleCount(ID2D1DeviceContext1 *iface, + ID2D1Effect *effect, UINT32 *rect_count) +{ + FIXME("iface %p, effect %p, rect_count %p stub!\n", iface, effect, rect_count); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_GetEffectInvalidRectangles(ID2D1DeviceContext1 *iface, + ID2D1Effect *effect, D2D1_RECT_F *rectangles, UINT32 rect_count) +{ + FIXME("iface %p, effect %p, rectangles %p, rect_count %u stub!\n", iface, effect, rectangles, rect_count); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_GetEffectRequiredInputRectangles(ID2D1DeviceContext1 *iface, + ID2D1Effect *effect, const D2D1_RECT_F *image_rect, const D2D1_EFFECT_INPUT_DESCRIPTION *desc, + D2D1_RECT_F *input_rect, UINT32 input_count) +{ + FIXME("iface %p, effect %p, image_rect %s, desc %p, input_rect %p, input_count %u stub!\n", + iface, effect, debug_d2d_rect_f(image_rect), desc, input_rect, input_count); + + return E_NOTIMPL; +} + +static void STDMETHODCALLTYPE d2d_device_context_ID2D1DeviceContext_FillOpacityMask(ID2D1DeviceContext1 *iface, + ID2D1Bitmap *mask, ID2D1Brush *brush, const D2D1_RECT_F *dst_rect, const D2D1_RECT_F *src_rect) +{ + struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface); + + FIXME("iface %p, mask %p, brush %p, dst_rect %s, src_rect %s stub!\n", + iface, mask, brush, debug_d2d_rect_f(dst_rect), debug_d2d_rect_f(src_rect)); + + if (FAILED(context->error.code)) + return; + + if (context->drawing_state.antialiasMode != D2D1_ANTIALIAS_MODE_ALIASED) + { + d2d_device_context_set_error(context, D2DERR_WRONG_STATE); + return; + } + + if (context->target.type == D2D_TARGET_COMMAND_LIST) + d2d_command_list_fill_opacity_mask(context->target.command_list, context, mask, brush, dst_rect, src_rect); +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateFilledGeometryRealization(ID2D1DeviceContext1 *iface, + ID2D1Geometry *geometry, float tolerance, ID2D1GeometryRealization **realization) +{ + FIXME("iface %p, geometry %p, tolerance %.8e, realization %p stub!\n", iface, geometry, tolerance, + realization); + + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateStrokedGeometryRealization(ID2D1DeviceContext1 *iface, + ID2D1Geometry *geometry, float tolerance, float stroke_width, ID2D1StrokeStyle *stroke_style, + ID2D1GeometryRealization **realization) +{ + FIXME("iface %p, geometry %p, tolerance %.8e, stroke_width %.8e, stroke_style %p, realization %p stub!\n", + iface, geometry, tolerance, stroke_width, stroke_style, realization); + + return E_NOTIMPL; +} + +static void STDMETHODCALLTYPE d2d_device_context_DrawGeometryRealization(ID2D1DeviceContext1 *iface, + ID2D1GeometryRealization *realization, ID2D1Brush *brush) +{ + FIXME("iface %p, realization %p, brush %p stub!\n", iface, realization, brush); +} + +static const struct ID2D1DeviceContext1Vtbl d2d_device_context_vtbl = +{ + d2d_device_context_QueryInterface, + d2d_device_context_AddRef, + d2d_device_context_Release, + d2d_device_context_GetFactory, + d2d_device_context_CreateBitmap, + d2d_device_context_CreateBitmapFromWicBitmap, + d2d_device_context_CreateSharedBitmap, + d2d_device_context_CreateBitmapBrush, + d2d_device_context_CreateSolidColorBrush, + d2d_device_context_CreateGradientStopCollection, + d2d_device_context_CreateLinearGradientBrush, + d2d_device_context_CreateRadialGradientBrush, + d2d_device_context_CreateCompatibleRenderTarget, + d2d_device_context_CreateLayer, + d2d_device_context_CreateMesh, + d2d_device_context_DrawLine, + d2d_device_context_DrawRectangle, + d2d_device_context_FillRectangle, + d2d_device_context_DrawRoundedRectangle, + d2d_device_context_FillRoundedRectangle, + d2d_device_context_DrawEllipse, + d2d_device_context_FillEllipse, + d2d_device_context_DrawGeometry, + d2d_device_context_FillGeometry, + d2d_device_context_FillMesh, + d2d_device_context_FillOpacityMask, + d2d_device_context_DrawBitmap, + d2d_device_context_DrawText, + d2d_device_context_DrawTextLayout, + d2d_device_context_DrawGlyphRun, + d2d_device_context_SetTransform, + d2d_device_context_GetTransform, + d2d_device_context_SetAntialiasMode, + d2d_device_context_GetAntialiasMode, + d2d_device_context_SetTextAntialiasMode, + d2d_device_context_GetTextAntialiasMode, + d2d_device_context_SetTextRenderingParams, + d2d_device_context_GetTextRenderingParams, + d2d_device_context_SetTags, + d2d_device_context_GetTags, + d2d_device_context_PushLayer, + d2d_device_context_PopLayer, + d2d_device_context_Flush, + d2d_device_context_SaveDrawingState, + d2d_device_context_RestoreDrawingState, + d2d_device_context_PushAxisAlignedClip, + d2d_device_context_PopAxisAlignedClip, + d2d_device_context_Clear, + d2d_device_context_BeginDraw, + d2d_device_context_EndDraw, + d2d_device_context_GetPixelFormat, + d2d_device_context_SetDpi, + d2d_device_context_GetDpi, + d2d_device_context_GetSize, + d2d_device_context_GetPixelSize, + d2d_device_context_GetMaximumBitmapSize, + d2d_device_context_IsSupported, + d2d_device_context_ID2D1DeviceContext_CreateBitmap, + d2d_device_context_ID2D1DeviceContext_CreateBitmapFromWicBitmap, + d2d_device_context_CreateColorContext, + d2d_device_context_CreateColorContextFromFilename, + d2d_device_context_CreateColorContextFromWicColorContext, + d2d_device_context_CreateBitmapFromDxgiSurface, + d2d_device_context_CreateEffect, + d2d_device_context_ID2D1DeviceContext_CreateGradientStopCollection, + d2d_device_context_CreateImageBrush, + d2d_device_context_ID2D1DeviceContext_CreateBitmapBrush, + d2d_device_context_CreateCommandList, + d2d_device_context_IsDxgiFormatSupported, + d2d_device_context_IsBufferPrecisionSupported, + d2d_device_context_GetImageLocalBounds, + d2d_device_context_GetImageWorldBounds, + d2d_device_context_GetGlyphRunWorldBounds, + d2d_device_context_GetDevice, + d2d_device_context_SetTarget, + d2d_device_context_GetTarget, + d2d_device_context_SetRenderingControls, + d2d_device_context_GetRenderingControls, + d2d_device_context_SetPrimitiveBlend, + d2d_device_context_GetPrimitiveBlend, + d2d_device_context_SetUnitMode, + d2d_device_context_GetUnitMode, + d2d_device_context_ID2D1DeviceContext_DrawGlyphRun, + d2d_device_context_DrawImage, + d2d_device_context_DrawGdiMetafile, + d2d_device_context_ID2D1DeviceContext_DrawBitmap, + d2d_device_context_ID2D1DeviceContext_PushLayer, + d2d_device_context_InvalidateEffectInputRectangle, + d2d_device_context_GetEffectInvalidRectangleCount, + d2d_device_context_GetEffectInvalidRectangles, + d2d_device_context_GetEffectRequiredInputRectangles, + d2d_device_context_ID2D1DeviceContext_FillOpacityMask, + d2d_device_context_CreateFilledGeometryRealization, + d2d_device_context_CreateStrokedGeometryRealization, + d2d_device_context_DrawGeometryRealization, +}; + +static inline struct d2d_device_context *impl_from_IDWriteTextRenderer(IDWriteTextRenderer *iface) +{ + return CONTAINING_RECORD(iface, struct d2d_device_context, IDWriteTextRenderer_iface); +} + +static HRESULT STDMETHODCALLTYPE d2d_text_renderer_QueryInterface(IDWriteTextRenderer *iface, REFIID iid, void **out) +{ + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IDWriteTextRenderer) + || IsEqualGUID(iid, &IID_IDWritePixelSnapping) + || IsEqualGUID(iid, &IID_IUnknown)) + { + IDWriteTextRenderer_AddRef(iface); + *out = iface; + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG STDMETHODCALLTYPE d2d_text_renderer_AddRef(IDWriteTextRenderer *iface) +{ + struct d2d_device_context *render_target = impl_from_IDWriteTextRenderer(iface); + + TRACE("iface %p.\n", iface); + + return d2d_device_context_AddRef(&render_target->ID2D1DeviceContext1_iface); +} + +static ULONG STDMETHODCALLTYPE d2d_text_renderer_Release(IDWriteTextRenderer *iface) +{ + struct d2d_device_context *render_target = impl_from_IDWriteTextRenderer(iface); + + TRACE("iface %p.\n", iface); + + return d2d_device_context_Release(&render_target->ID2D1DeviceContext1_iface); +} + +static HRESULT STDMETHODCALLTYPE d2d_text_renderer_IsPixelSnappingDisabled(IDWriteTextRenderer *iface, + void *ctx, BOOL *disabled) +{ + struct d2d_draw_text_layout_ctx *context = ctx; + + TRACE("iface %p, ctx %p, disabled %p.\n", iface, ctx, disabled); + + *disabled = context->options & D2D1_DRAW_TEXT_OPTIONS_NO_SNAP; + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE d2d_text_renderer_GetCurrentTransform(IDWriteTextRenderer *iface, + void *ctx, DWRITE_MATRIX *transform) +{ + struct d2d_device_context *render_target = impl_from_IDWriteTextRenderer(iface); + + TRACE("iface %p, ctx %p, transform %p.\n", iface, ctx, transform); + + d2d_device_context_GetTransform(&render_target->ID2D1DeviceContext1_iface, (D2D1_MATRIX_3X2_F *)transform); + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE d2d_text_renderer_GetPixelsPerDip(IDWriteTextRenderer *iface, void *ctx, float *ppd) +{ + struct d2d_device_context *render_target = impl_from_IDWriteTextRenderer(iface); + + TRACE("iface %p, ctx %p, ppd %p.\n", iface, ctx, ppd); + + *ppd = render_target->desc.dpiY / 96.0f; + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE d2d_text_renderer_DrawGlyphRun(IDWriteTextRenderer *iface, void *ctx, + float baseline_origin_x, float baseline_origin_y, DWRITE_MEASURING_MODE measuring_mode, + const DWRITE_GLYPH_RUN *glyph_run, const DWRITE_GLYPH_RUN_DESCRIPTION *glyph_run_desc, IUnknown *effect) +{ + struct d2d_device_context *render_target = impl_from_IDWriteTextRenderer(iface); + D2D1_POINT_2F baseline_origin = {baseline_origin_x, baseline_origin_y}; + struct d2d_draw_text_layout_ctx *context = ctx; + BOOL color_font = FALSE; + ID2D1Brush *brush; + + TRACE("iface %p, ctx %p, baseline_origin_x %.8e, baseline_origin_y %.8e, " + "measuring_mode %#x, glyph_run %p, glyph_run_desc %p, effect %p.\n", + iface, ctx, baseline_origin_x, baseline_origin_y, + measuring_mode, glyph_run, glyph_run_desc, effect); + + if (context->options & ~(D2D1_DRAW_TEXT_OPTIONS_NO_SNAP | D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT)) + FIXME("Ignoring options %#x.\n", context->options); + + brush = d2d_draw_get_text_brush(context, effect); + + TRACE("%s\n", debugstr_wn(glyph_run_desc->string, glyph_run_desc->stringLength)); + + if (context->options & D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT) + { + IDWriteFontFace2 *fontface; + + if (SUCCEEDED(IDWriteFontFace_QueryInterface(glyph_run->fontFace, + &IID_IDWriteFontFace2, (void **)&fontface))) + { + color_font = IDWriteFontFace2_IsColorFont(fontface); + IDWriteFontFace2_Release(fontface); + } + } + + if (color_font) + { + IDWriteColorGlyphRunEnumerator *layers; + IDWriteFactory2 *dwrite_factory; + HRESULT hr; + + if (FAILED(hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, &IID_IDWriteFactory2, + (IUnknown **)&dwrite_factory))) + { + ERR("Failed to create dwrite factory, hr %#lx.\n", hr); + ID2D1Brush_Release(brush); + return hr; + } + + hr = IDWriteFactory2_TranslateColorGlyphRun(dwrite_factory, baseline_origin_x, baseline_origin_y, + glyph_run, glyph_run_desc, measuring_mode, (DWRITE_MATRIX *)&render_target->drawing_state.transform, 0, &layers); + IDWriteFactory2_Release(dwrite_factory); + if (FAILED(hr)) + { + ERR("Failed to create colour glyph run enumerator, hr %#lx.\n", hr); + ID2D1Brush_Release(brush); + return hr; + } + + for (;;) + { + const DWRITE_COLOR_GLYPH_RUN *color_run; + ID2D1Brush *color_brush; + D2D1_POINT_2F origin; + BOOL has_run = FALSE; + + if (FAILED(hr = IDWriteColorGlyphRunEnumerator_MoveNext(layers, &has_run))) + { + ERR("Failed to switch colour glyph layer, hr %#lx.\n", hr); + break; + } + + if (!has_run) + break; + + if (FAILED(hr = IDWriteColorGlyphRunEnumerator_GetCurrentRun(layers, &color_run))) + { + ERR("Failed to get current colour run, hr %#lx.\n", hr); + break; + } + + if (color_run->paletteIndex == 0xffff) + color_brush = brush; + else + { + if (FAILED(hr = d2d_device_context_CreateSolidColorBrush(&render_target->ID2D1DeviceContext1_iface, + &color_run->runColor, NULL, (ID2D1SolidColorBrush **)&color_brush))) + { + ERR("Failed to create solid colour brush, hr %#lx.\n", hr); + break; + } + } + + origin.x = color_run->baselineOriginX; + origin.y = color_run->baselineOriginY; + d2d_device_context_draw_glyph_run(render_target, origin, &color_run->glyphRun, + color_run->glyphRunDescription, color_brush, measuring_mode); + + if (color_brush != brush) + ID2D1Brush_Release(color_brush); + } + + IDWriteColorGlyphRunEnumerator_Release(layers); + } + else + d2d_device_context_draw_glyph_run(render_target, baseline_origin, glyph_run, glyph_run_desc, + brush, measuring_mode); + + ID2D1Brush_Release(brush); + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE d2d_text_renderer_DrawUnderline(IDWriteTextRenderer *iface, void *ctx, + float baseline_origin_x, float baseline_origin_y, const DWRITE_UNDERLINE *underline, IUnknown *effect) +{ + struct d2d_device_context *render_target = impl_from_IDWriteTextRenderer(iface); + const D2D1_MATRIX_3X2_F *m = &render_target->drawing_state.transform; + struct d2d_draw_text_layout_ctx *context = ctx; + D2D1_ANTIALIAS_MODE prev_antialias_mode; + D2D1_POINT_2F start, end; + ID2D1Brush *brush; + float thickness; + + TRACE("iface %p, ctx %p, baseline_origin_x %.8e, baseline_origin_y %.8e, underline %p, effect %p\n", + iface, ctx, baseline_origin_x, baseline_origin_y, underline, effect); + + /* minimal thickness in DIPs that will result in at least 1 pixel thick line */ + thickness = max(96.0f / (render_target->desc.dpiY * sqrtf(m->_21 * m->_21 + m->_22 * m->_22)), + underline->thickness); + + brush = d2d_draw_get_text_brush(context, effect); + + start.x = baseline_origin_x; + start.y = baseline_origin_y + underline->offset + thickness / 2.0f; + end.x = start.x + underline->width; + end.y = start.y; + prev_antialias_mode = d2d_device_context_set_aa_mode_from_text_aa_mode(render_target); + d2d_device_context_DrawLine(&render_target->ID2D1DeviceContext1_iface, start, end, brush, thickness, NULL); + render_target->drawing_state.antialiasMode = prev_antialias_mode; + + ID2D1Brush_Release(brush); + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE d2d_text_renderer_DrawStrikethrough(IDWriteTextRenderer *iface, void *ctx, + float baseline_origin_x, float baseline_origin_y, const DWRITE_STRIKETHROUGH *strikethrough, IUnknown *effect) +{ + struct d2d_device_context *render_target = impl_from_IDWriteTextRenderer(iface); + const D2D1_MATRIX_3X2_F *m = &render_target->drawing_state.transform; + struct d2d_draw_text_layout_ctx *context = ctx; + D2D1_ANTIALIAS_MODE prev_antialias_mode; + D2D1_POINT_2F start, end; + ID2D1Brush *brush; + float thickness; + + TRACE("iface %p, ctx %p, baseline_origin_x %.8e, baseline_origin_y %.8e, strikethrough %p, effect %p.\n", + iface, ctx, baseline_origin_x, baseline_origin_y, strikethrough, effect); + + /* minimal thickness in DIPs that will result in at least 1 pixel thick line */ + thickness = max(96.0f / (render_target->desc.dpiY * sqrtf(m->_21 * m->_21 + m->_22 * m->_22)), + strikethrough->thickness); + + brush = d2d_draw_get_text_brush(context, effect); + + start.x = baseline_origin_x; + start.y = baseline_origin_y + strikethrough->offset + thickness / 2.0f; + end.x = start.x + strikethrough->width; + end.y = start.y; + prev_antialias_mode = d2d_device_context_set_aa_mode_from_text_aa_mode(render_target); + d2d_device_context_DrawLine(&render_target->ID2D1DeviceContext1_iface, start, end, brush, thickness, NULL); + render_target->drawing_state.antialiasMode = prev_antialias_mode; + + ID2D1Brush_Release(brush); + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE d2d_text_renderer_DrawInlineObject(IDWriteTextRenderer *iface, void *ctx, + float origin_x, float origin_y, IDWriteInlineObject *object, BOOL is_sideways, BOOL is_rtl, IUnknown *effect) +{ + struct d2d_draw_text_layout_ctx *context = ctx; + ID2D1Brush *brush; + HRESULT hr; + + TRACE("iface %p, ctx %p, origin_x %.8e, origin_y %.8e, object %p, is_sideways %#x, is_rtl %#x, effect %p.\n", + iface, ctx, origin_x, origin_y, object, is_sideways, is_rtl, effect); + + /* Inline objects may not pass effects all the way down, when using layout object internally for example. + This is how default trimming sign object in DirectWrite works - it does not use effect passed to Draw(), + and resulting DrawGlyphRun() is always called with NULL effect, however original effect is used and correct + brush is selected at Direct2D level. */ + brush = context->brush; + context->brush = d2d_draw_get_text_brush(context, effect); + + hr = IDWriteInlineObject_Draw(object, ctx, iface, origin_x, origin_y, is_sideways, is_rtl, effect); + + ID2D1Brush_Release(context->brush); + context->brush = brush; + + return hr; +} + +static const struct IDWriteTextRendererVtbl d2d_text_renderer_vtbl = +{ + d2d_text_renderer_QueryInterface, + d2d_text_renderer_AddRef, + d2d_text_renderer_Release, + d2d_text_renderer_IsPixelSnappingDisabled, + d2d_text_renderer_GetCurrentTransform, + d2d_text_renderer_GetPixelsPerDip, + d2d_text_renderer_DrawGlyphRun, + d2d_text_renderer_DrawUnderline, + d2d_text_renderer_DrawStrikethrough, + d2d_text_renderer_DrawInlineObject, +}; + +static inline struct d2d_device_context *impl_from_ID2D1GdiInteropRenderTarget(ID2D1GdiInteropRenderTarget *iface) +{ + return CONTAINING_RECORD(iface, struct d2d_device_context, ID2D1GdiInteropRenderTarget_iface); +} + +static HRESULT STDMETHODCALLTYPE d2d_gdi_interop_render_target_QueryInterface(ID2D1GdiInteropRenderTarget *iface, + REFIID iid, void **out) +{ + struct d2d_device_context *render_target = impl_from_ID2D1GdiInteropRenderTarget(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + return IUnknown_QueryInterface(render_target->outer_unknown, iid, out); +} + +static ULONG STDMETHODCALLTYPE d2d_gdi_interop_render_target_AddRef(ID2D1GdiInteropRenderTarget *iface) +{ + struct d2d_device_context *render_target = impl_from_ID2D1GdiInteropRenderTarget(iface); + + TRACE("iface %p.\n", iface); + + return IUnknown_AddRef(render_target->outer_unknown); +} + +static ULONG STDMETHODCALLTYPE d2d_gdi_interop_render_target_Release(ID2D1GdiInteropRenderTarget *iface) +{ + struct d2d_device_context *render_target = impl_from_ID2D1GdiInteropRenderTarget(iface); + + TRACE("iface %p.\n", iface); + + return IUnknown_Release(render_target->outer_unknown); +} + +static HRESULT d2d_gdi_interop_get_surface(struct d2d_device_context *context, IDXGISurface1 **surface) +{ + ID3D11Resource *resource; + HRESULT hr; + + if (context->target.type != D2D_TARGET_BITMAP) + { + FIXME("Unimplemented for target type %u.\n", context->target.type); + return E_NOTIMPL; + } + + if (!(context->target.bitmap->options & D2D1_BITMAP_OPTIONS_GDI_COMPATIBLE)) + return D2DERR_TARGET_NOT_GDI_COMPATIBLE; + + ID3D11RenderTargetView_GetResource(context->target.bitmap->rtv, &resource); + hr = ID3D11Resource_QueryInterface(resource, &IID_IDXGISurface1, (void **)surface); + ID3D11Resource_Release(resource); + if (FAILED(hr)) + { + *surface = NULL; + WARN("Failed to get DXGI surface, %#lx.\n", hr); + return hr; + } + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_gdi_interop_render_target_GetDC(ID2D1GdiInteropRenderTarget *iface, + D2D1_DC_INITIALIZE_MODE mode, HDC *dc) +{ + struct d2d_device_context *render_target = impl_from_ID2D1GdiInteropRenderTarget(iface); + IDXGISurface1 *surface; + HRESULT hr; + + TRACE("iface %p, mode %d, dc %p.\n", iface, mode, dc); + + *dc = NULL; + + if (render_target->target.hdc) + return D2DERR_WRONG_STATE; + + if (FAILED(hr = d2d_gdi_interop_get_surface(render_target, &surface))) + return hr; + + hr = IDXGISurface1_GetDC(surface, mode != D2D1_DC_INITIALIZE_MODE_COPY, &render_target->target.hdc); + IDXGISurface1_Release(surface); + + if (SUCCEEDED(hr)) + *dc = render_target->target.hdc; + + return hr; +} + +static HRESULT STDMETHODCALLTYPE d2d_gdi_interop_render_target_ReleaseDC(ID2D1GdiInteropRenderTarget *iface, + const RECT *update) +{ + struct d2d_device_context *render_target = impl_from_ID2D1GdiInteropRenderTarget(iface); + IDXGISurface1 *surface; + RECT update_rect; + HRESULT hr; + + TRACE("iface %p, update rect %s.\n", iface, wine_dbgstr_rect(update)); + + if (!render_target->target.hdc) + return D2DERR_WRONG_STATE; + + if (FAILED(hr = d2d_gdi_interop_get_surface(render_target, &surface))) + return hr; + + render_target->target.hdc = NULL; + if (update) + update_rect = *update; + hr = IDXGISurface1_ReleaseDC(surface, update ? &update_rect : NULL); + IDXGISurface1_Release(surface); + + return hr; +} + +static const struct ID2D1GdiInteropRenderTargetVtbl d2d_gdi_interop_render_target_vtbl = +{ + d2d_gdi_interop_render_target_QueryInterface, + d2d_gdi_interop_render_target_AddRef, + d2d_gdi_interop_render_target_Release, + d2d_gdi_interop_render_target_GetDC, + d2d_gdi_interop_render_target_ReleaseDC, +}; + +static HRESULT d2d_device_context_init(struct d2d_device_context *render_target, + struct d2d_device *device, IUnknown *outer_unknown, const struct d2d_device_context_ops *ops) +{ + D3D11_SUBRESOURCE_DATA buffer_data; + struct d2d_device *device_impl; + IDWriteFactory *dwrite_factory; + D3D11_RASTERIZER_DESC rs_desc; + D3D11_BUFFER_DESC buffer_desc; + unsigned int i; + HRESULT hr; + + static const D3D11_INPUT_ELEMENT_DESC il_desc_outline[] = + { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"PREV", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"NEXT", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0}, + }; + static const D3D11_INPUT_ELEMENT_DESC il_desc_curve_outline[] = + { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"P", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"P", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"P", 2, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"PREV", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"NEXT", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 40, D3D11_INPUT_PER_VERTEX_DATA, 0}, + }; + static const D3D11_INPUT_ELEMENT_DESC il_desc_triangle[] = + { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, + }; + static const D3D11_INPUT_ELEMENT_DESC il_desc_curve[] = + { + {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0}, + }; + static const DWORD vs_code_outline[] = + { +#if 0 + float3x2 transform_geometry; + float stroke_width; + float4 transform_rtx; + float4 transform_rty; + + struct output + { + float2 p : WORLD_POSITION; + float4 b : BEZIER; + nointerpolation float2x2 stroke_transform : STROKE_TRANSFORM; + float4 position : SV_POSITION; + }; + + /* The lines PₚᵣₑᵥP₀ and P₀Pₙₑₓₜ, both offset by ±½w, intersect each other at: + * + * Pᵢ = P₀ ± w · ½q⃑ᵢ. + * + * Where: + * + * q⃑ᵢ = q̂ₚᵣₑᵥ⊥ + tan(½θ) · -q̂ₚᵣₑᵥ + * θ = ∠PₚᵣₑᵥP₀Pₙₑₓₜ + * q⃑ₚᵣₑᵥ = P₀ - Pₚᵣₑᵥ */ + void main(float2 position : POSITION, float2 prev : PREV, float2 next : NEXT, out struct output o) + { + float2 q_prev, q_next, v_p, q_i; + float2x2 geom; + float l; + + o.stroke_transform = float2x2(transform_rtx.xy, transform_rty.xy) * stroke_width * 0.5f; + + geom = float2x2(transform_geometry._11_21, transform_geometry._12_22); + q_prev = normalize(mul(geom, prev)); + q_next = normalize(mul(geom, next)); + + /* tan(½θ) = sin(θ) / (1 + cos(θ)) + * = (q̂ₚᵣₑᵥ⊥ · q̂ₙₑₓₜ) / (1 + (q̂ₚᵣₑᵥ · q̂ₙₑₓₜ)) */ + v_p = float2(-q_prev.y, q_prev.x); + l = -dot(v_p, q_next) / (1.0f + dot(q_prev, q_next)); + q_i = l * q_prev + v_p; + + o.b = float4(0.0, 0.0, 0.0, 0.0); + + o.p = mul(float3(position, 1.0f), transform_geometry) + stroke_width * 0.5f * q_i; + position = mul(float2x3(transform_rtx.xyz, transform_rty.xyz), float3(o.p, 1.0f)) + * float2(transform_rtx.w, transform_rty.w); + o.position = float4(position + float2(-1.0f, 1.0f), 0.0f, 1.0f); + } +#endif + 0x43425844, 0xfb16cd75, 0xf5ec3e80, 0xceacf250, 0x91d29d18, 0x00000001, 0x00000608, 0x00000003, + 0x0000002c, 0x00000098, 0x00000154, 0x4e475349, 0x00000064, 0x00000003, 0x00000008, 0x00000050, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000303, 0x00000059, 0x00000000, 0x00000000, + 0x00000003, 0x00000001, 0x00000303, 0x0000005e, 0x00000000, 0x00000000, 0x00000003, 0x00000002, + 0x00000303, 0x49534f50, 0x4e4f4954, 0x45525000, 0x454e0056, 0xab005458, 0x4e47534f, 0x000000b4, + 0x00000005, 0x00000008, 0x00000080, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000c03, + 0x0000008f, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f, 0x00000096, 0x00000000, + 0x00000000, 0x00000003, 0x00000002, 0x00000c03, 0x00000096, 0x00000001, 0x00000000, 0x00000003, + 0x00000003, 0x00000c03, 0x000000a7, 0x00000000, 0x00000001, 0x00000003, 0x00000004, 0x0000000f, + 0x4c524f57, 0x4f505f44, 0x49544953, 0x42004e4f, 0x45495a45, 0x54530052, 0x454b4f52, 0x4152545f, + 0x4f46534e, 0x53004d52, 0x4f505f56, 0x49544953, 0xab004e4f, 0x52444853, 0x000004ac, 0x00010040, + 0x0000012b, 0x04000059, 0x00208e46, 0x00000000, 0x00000004, 0x0300005f, 0x00101032, 0x00000000, + 0x0300005f, 0x00101032, 0x00000001, 0x0300005f, 0x00101032, 0x00000002, 0x03000065, 0x00102032, + 0x00000000, 0x03000065, 0x001020f2, 0x00000001, 0x03000065, 0x00102032, 0x00000002, 0x03000065, + 0x00102032, 0x00000003, 0x04000067, 0x001020f2, 0x00000004, 0x00000001, 0x02000068, 0x00000003, + 0x0800000f, 0x00100012, 0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000002, + 0x0800000f, 0x00100022, 0x00000000, 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000002, + 0x0700000f, 0x00100042, 0x00000000, 0x00100046, 0x00000000, 0x00100046, 0x00000000, 0x05000044, + 0x00100042, 0x00000000, 0x0010002a, 0x00000000, 0x07000038, 0x00100032, 0x00000000, 0x00100aa6, + 0x00000000, 0x00100046, 0x00000000, 0x0800000f, 0x00100012, 0x00000001, 0x00208046, 0x00000000, + 0x00000000, 0x00101046, 0x00000001, 0x0800000f, 0x00100022, 0x00000001, 0x00208046, 0x00000000, + 0x00000001, 0x00101046, 0x00000001, 0x0700000f, 0x00100042, 0x00000000, 0x00100046, 0x00000001, + 0x00100046, 0x00000001, 0x05000044, 0x00100042, 0x00000000, 0x0010002a, 0x00000000, 0x07000038, + 0x00100032, 0x00000001, 0x00100aa6, 0x00000000, 0x00100046, 0x00000001, 0x06000036, 0x001000c2, + 0x00000001, 0x80100556, 0x00000041, 0x00000001, 0x0700000f, 0x00100042, 0x00000000, 0x00100a26, + 0x00000001, 0x00100046, 0x00000000, 0x0700000f, 0x00100012, 0x00000000, 0x00100046, 0x00000001, + 0x00100046, 0x00000000, 0x07000000, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, + 0x3f800000, 0x0800000e, 0x00100012, 0x00000000, 0x8010002a, 0x00000041, 0x00000000, 0x0010000a, + 0x00000000, 0x09000032, 0x00100032, 0x00000000, 0x00100006, 0x00000000, 0x00100046, 0x00000001, + 0x00100f36, 0x00000001, 0x08000038, 0x00100042, 0x00000000, 0x0020803a, 0x00000000, 0x00000001, + 0x00004001, 0x3f000000, 0x05000036, 0x00100032, 0x00000001, 0x00101046, 0x00000000, 0x05000036, + 0x00100042, 0x00000001, 0x00004001, 0x3f800000, 0x08000010, 0x00100012, 0x00000002, 0x00100246, + 0x00000001, 0x00208246, 0x00000000, 0x00000000, 0x08000010, 0x00100022, 0x00000002, 0x00100246, + 0x00000001, 0x00208246, 0x00000000, 0x00000001, 0x09000032, 0x00100032, 0x00000000, 0x00100aa6, + 0x00000000, 0x00100046, 0x00000000, 0x00100046, 0x00000002, 0x05000036, 0x00102032, 0x00000000, + 0x00100046, 0x00000000, 0x08000036, 0x001020f2, 0x00000001, 0x00004002, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x06000036, 0x00100032, 0x00000001, 0x00208046, 0x00000000, 0x00000002, + 0x06000036, 0x001000c2, 0x00000001, 0x00208406, 0x00000000, 0x00000003, 0x08000038, 0x001000f2, + 0x00000001, 0x00100e46, 0x00000001, 0x00208ff6, 0x00000000, 0x00000001, 0x0a000038, 0x001000f2, + 0x00000001, 0x00100e46, 0x00000001, 0x00004002, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, + 0x05000036, 0x00102032, 0x00000002, 0x00100086, 0x00000001, 0x05000036, 0x00102032, 0x00000003, + 0x001005d6, 0x00000001, 0x05000036, 0x00100042, 0x00000000, 0x00004001, 0x3f800000, 0x08000010, + 0x00100082, 0x00000000, 0x00208246, 0x00000000, 0x00000002, 0x00100246, 0x00000000, 0x08000010, + 0x00100012, 0x00000000, 0x00208246, 0x00000000, 0x00000003, 0x00100246, 0x00000000, 0x08000038, + 0x00100022, 0x00000000, 0x0010000a, 0x00000000, 0x0020803a, 0x00000000, 0x00000003, 0x08000038, + 0x00100012, 0x00000000, 0x0010003a, 0x00000000, 0x0020803a, 0x00000000, 0x00000002, 0x0a000000, + 0x00102032, 0x00000004, 0x00100046, 0x00000000, 0x00004002, 0xbf800000, 0x3f800000, 0x00000000, + 0x00000000, 0x08000036, 0x001020c2, 0x00000004, 0x00004002, 0x00000000, 0x00000000, 0x00000000, + 0x3f800000, 0x0100003e, + }; + /* ⎡p0.x p0.y 1⎤ + * A = ⎢p1.x p1.y 1⎥ + * ⎣p2.x p2.y 1⎦ + * + * ⎡0 0⎤ + * B = ⎢½ 0⎥ + * ⎣1 1⎦ + * + * A' = ⎡p1.x-p0.x p1.y-p0.y⎤ + * ⎣p2.x-p0.x p2.y-p0.y⎦ + * + * B' = ⎡½ 0⎤ + * ⎣1 1⎦ + * + * A'T = B' + * T = A'⁻¹B' + */ + static const DWORD vs_code_bezier_outline[] = + { +#if 0 + float3x2 transform_geometry; + float stroke_width; + float4 transform_rtx; + float4 transform_rty; + + struct output + { + float2 p : WORLD_POSITION; + float4 b : BEZIER; + nointerpolation float2x2 stroke_transform : STROKE_TRANSFORM; + float4 position : SV_POSITION; + }; + + void main(float2 position : POSITION, float2 p0 : P0, float2 p1 : P1, float2 p2 : P2, + float2 prev : PREV, float2 next : NEXT, out struct output o) + { + float2 q_prev, q_next, v_p, q_i, p; + float2x2 geom, rt; + float l; + + geom = float2x2(transform_geometry._11_21, transform_geometry._12_22); + rt = float2x2(transform_rtx.xy, transform_rty.xy); + o.stroke_transform = rt * stroke_width * 0.5f; + + p = mul(geom, position); + p0 = mul(geom, p0); + p1 = mul(geom, p1); + p2 = mul(geom, p2); + + p -= p0; + p1 -= p0; + p2 -= p0; + + q_prev = normalize(mul(geom, prev)); + q_next = normalize(mul(geom, next)); + + v_p = float2(-q_prev.y, q_prev.x); + l = -dot(v_p, q_next) / (1.0f + dot(q_prev, q_next)); + q_i = l * q_prev + v_p; + p += 0.5f * stroke_width * q_i; + + v_p = mul(rt, p2); + v_p = normalize(float2(-v_p.y, v_p.x)); + if (abs(dot(mul(rt, p1), v_p)) < 1.0f) + { + o.b.xzw = float3(0.0f, 0.0f, 0.0f); + o.b.y = dot(mul(rt, p), v_p); + } + else + { + o.b.zw = sign(dot(mul(rt, p1), v_p)) * v_p; + v_p = -float2(-p.y, p.x) / dot(float2(-p1.y, p1.x), p2); + o.b.x = dot(v_p, p1 - 0.5f * p2); + o.b.y = dot(v_p, p1); + } + + o.p = mul(float3(position, 1.0f), transform_geometry) + 0.5f * stroke_width * q_i; + position = mul(float2x3(transform_rtx.xyz, transform_rty.xyz), float3(o.p, 1.0f)) + * float2(transform_rtx.w, transform_rty.w); + o.position = float4(position + float2(-1.0f, 1.0f), 0.0f, 1.0f); + } +#endif + 0x43425844, 0x356a0c5f, 0x8e4ba153, 0xe52cf793, 0xa6b774ea, 0x00000001, 0x00000afc, 0x00000003, + 0x0000002c, 0x000000e4, 0x000001a0, 0x4e475349, 0x000000b0, 0x00000006, 0x00000008, 0x00000098, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000303, 0x000000a1, 0x00000000, 0x00000000, + 0x00000003, 0x00000001, 0x00000303, 0x000000a1, 0x00000001, 0x00000000, 0x00000003, 0x00000002, + 0x00000303, 0x000000a1, 0x00000002, 0x00000000, 0x00000003, 0x00000003, 0x00000303, 0x000000a3, + 0x00000000, 0x00000000, 0x00000003, 0x00000004, 0x00000303, 0x000000a8, 0x00000000, 0x00000000, + 0x00000003, 0x00000005, 0x00000303, 0x49534f50, 0x4e4f4954, 0x50005000, 0x00564552, 0x5458454e, + 0xababab00, 0x4e47534f, 0x000000b4, 0x00000005, 0x00000008, 0x00000080, 0x00000000, 0x00000000, + 0x00000003, 0x00000000, 0x00000c03, 0x0000008f, 0x00000000, 0x00000000, 0x00000003, 0x00000001, + 0x0000000f, 0x00000096, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000c03, 0x00000096, + 0x00000001, 0x00000000, 0x00000003, 0x00000003, 0x00000c03, 0x000000a7, 0x00000000, 0x00000001, + 0x00000003, 0x00000004, 0x0000000f, 0x4c524f57, 0x4f505f44, 0x49544953, 0x42004e4f, 0x45495a45, + 0x54530052, 0x454b4f52, 0x4152545f, 0x4f46534e, 0x53004d52, 0x4f505f56, 0x49544953, 0xab004e4f, + 0x52444853, 0x00000954, 0x00010040, 0x00000255, 0x04000059, 0x00208e46, 0x00000000, 0x00000004, + 0x0300005f, 0x00101032, 0x00000000, 0x0300005f, 0x00101032, 0x00000001, 0x0300005f, 0x00101032, + 0x00000002, 0x0300005f, 0x00101032, 0x00000003, 0x0300005f, 0x00101032, 0x00000004, 0x0300005f, + 0x00101032, 0x00000005, 0x03000065, 0x00102032, 0x00000000, 0x03000065, 0x001020f2, 0x00000001, + 0x03000065, 0x00102032, 0x00000002, 0x03000065, 0x00102032, 0x00000003, 0x04000067, 0x001020f2, + 0x00000004, 0x00000001, 0x02000068, 0x00000006, 0x0800000f, 0x00100012, 0x00000000, 0x00208046, + 0x00000000, 0x00000000, 0x00101046, 0x00000005, 0x0800000f, 0x00100022, 0x00000000, 0x00208046, + 0x00000000, 0x00000001, 0x00101046, 0x00000005, 0x0700000f, 0x00100042, 0x00000000, 0x00100046, + 0x00000000, 0x00100046, 0x00000000, 0x05000044, 0x00100042, 0x00000000, 0x0010002a, 0x00000000, + 0x07000038, 0x00100032, 0x00000000, 0x00100aa6, 0x00000000, 0x00100046, 0x00000000, 0x0800000f, + 0x00100012, 0x00000001, 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000004, 0x0800000f, + 0x00100022, 0x00000001, 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000004, 0x0700000f, + 0x00100042, 0x00000000, 0x00100046, 0x00000001, 0x00100046, 0x00000001, 0x05000044, 0x00100042, + 0x00000000, 0x0010002a, 0x00000000, 0x07000038, 0x00100032, 0x00000001, 0x00100aa6, 0x00000000, + 0x00100046, 0x00000001, 0x06000036, 0x001000c2, 0x00000001, 0x80100556, 0x00000041, 0x00000001, + 0x0700000f, 0x00100042, 0x00000000, 0x00100a26, 0x00000001, 0x00100046, 0x00000000, 0x0700000f, + 0x00100012, 0x00000000, 0x00100046, 0x00000001, 0x00100046, 0x00000000, 0x07000000, 0x00100012, + 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000, 0x0800000e, 0x00100012, 0x00000000, + 0x8010002a, 0x00000041, 0x00000000, 0x0010000a, 0x00000000, 0x09000032, 0x00100032, 0x00000000, + 0x00100006, 0x00000000, 0x00100046, 0x00000001, 0x00100f36, 0x00000001, 0x05000036, 0x00100032, + 0x00000001, 0x00101046, 0x00000000, 0x05000036, 0x00100042, 0x00000001, 0x00004001, 0x3f800000, + 0x08000010, 0x00100012, 0x00000002, 0x00100246, 0x00000001, 0x00208246, 0x00000000, 0x00000000, + 0x08000010, 0x00100022, 0x00000002, 0x00100246, 0x00000001, 0x00208246, 0x00000000, 0x00000001, + 0x08000038, 0x00100042, 0x00000000, 0x0020803a, 0x00000000, 0x00000001, 0x00004001, 0x3f000000, + 0x09000032, 0x00100032, 0x00000001, 0x00100aa6, 0x00000000, 0x00100046, 0x00000000, 0x00100046, + 0x00000002, 0x05000036, 0x00102032, 0x00000000, 0x00100046, 0x00000001, 0x0800000f, 0x00100012, + 0x00000002, 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000000, 0x0800000f, 0x00100022, + 0x00000002, 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000000, 0x0800000f, 0x00100012, + 0x00000003, 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000001, 0x0800000f, 0x00100022, + 0x00000003, 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000001, 0x08000000, 0x00100032, + 0x00000002, 0x00100046, 0x00000002, 0x80100046, 0x00000041, 0x00000003, 0x09000032, 0x00100032, + 0x00000000, 0x00100aa6, 0x00000000, 0x00100046, 0x00000000, 0x00100046, 0x00000002, 0x0800000f, + 0x00100012, 0x00000002, 0x00208046, 0x00000000, 0x00000002, 0x00100046, 0x00000000, 0x0800000f, + 0x00100022, 0x00000002, 0x00208046, 0x00000000, 0x00000003, 0x00100046, 0x00000000, 0x0800000f, + 0x00100012, 0x00000004, 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000003, 0x0800000f, + 0x00100022, 0x00000004, 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000003, 0x08000000, + 0x001000c2, 0x00000002, 0x80100406, 0x00000041, 0x00000003, 0x00100406, 0x00000004, 0x0800000f, + 0x00100082, 0x00000000, 0x00208046, 0x00000000, 0x00000003, 0x00100ae6, 0x00000002, 0x06000036, + 0x00100042, 0x00000003, 0x8010003a, 0x00000041, 0x00000000, 0x0800000f, 0x00100082, 0x00000003, + 0x00208046, 0x00000000, 0x00000002, 0x00100ae6, 0x00000002, 0x0700000f, 0x00100082, 0x00000000, + 0x00100ae6, 0x00000003, 0x00100ae6, 0x00000003, 0x05000044, 0x00100082, 0x00000000, 0x0010003a, + 0x00000000, 0x07000038, 0x001000c2, 0x00000003, 0x00100ff6, 0x00000000, 0x00100ea6, 0x00000003, + 0x0700000f, 0x00100022, 0x00000004, 0x00100046, 0x00000002, 0x00100ae6, 0x00000003, 0x06000036, + 0x00100042, 0x00000000, 0x8010001a, 0x00000041, 0x00000000, 0x0800000f, 0x00100012, 0x00000002, + 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000002, 0x0800000f, 0x00100022, 0x00000002, + 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000002, 0x08000000, 0x00100032, 0x00000005, + 0x80100046, 0x00000041, 0x00000003, 0x00100046, 0x00000002, 0x06000036, 0x00100042, 0x00000005, + 0x8010001a, 0x00000041, 0x00000005, 0x0700000f, 0x00100022, 0x00000000, 0x00100a26, 0x00000005, + 0x00100ae6, 0x00000002, 0x0d000032, 0x00100032, 0x00000002, 0x80100ae6, 0x00000041, 0x00000002, + 0x00004002, 0x3f000000, 0x3f000000, 0x00000000, 0x00000000, 0x00100046, 0x00000005, 0x0800000e, + 0x00100032, 0x00000000, 0x80100a26, 0x00000041, 0x00000000, 0x00100556, 0x00000000, 0x0700000f, + 0x00100012, 0x00000002, 0x00100046, 0x00000000, 0x00100046, 0x00000002, 0x0700000f, 0x00100022, + 0x00000002, 0x00100046, 0x00000000, 0x00100046, 0x00000005, 0x0800000f, 0x00100012, 0x00000000, + 0x00208046, 0x00000000, 0x00000002, 0x00100046, 0x00000005, 0x0800000f, 0x00100022, 0x00000000, + 0x00208046, 0x00000000, 0x00000003, 0x00100046, 0x00000005, 0x0700000f, 0x00100012, 0x00000000, + 0x00100046, 0x00000000, 0x00100ae6, 0x00000003, 0x07000031, 0x00100022, 0x00000000, 0x00004001, + 0x00000000, 0x0010000a, 0x00000000, 0x07000031, 0x00100042, 0x00000000, 0x0010000a, 0x00000000, + 0x00004001, 0x00000000, 0x08000031, 0x00100012, 0x00000000, 0x8010000a, 0x00000081, 0x00000000, + 0x00004001, 0x3f800000, 0x0800001e, 0x00100022, 0x00000000, 0x8010001a, 0x00000041, 0x00000000, + 0x0010002a, 0x00000000, 0x0500002b, 0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x07000038, + 0x001000c2, 0x00000002, 0x00100ea6, 0x00000003, 0x00100556, 0x00000000, 0x08000036, 0x001000d2, + 0x00000004, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x09000037, 0x001020f2, + 0x00000001, 0x00100006, 0x00000000, 0x00100e46, 0x00000004, 0x00100e46, 0x00000002, 0x06000036, + 0x00100032, 0x00000000, 0x00208046, 0x00000000, 0x00000002, 0x06000036, 0x001000c2, 0x00000000, + 0x00208406, 0x00000000, 0x00000003, 0x08000038, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000, + 0x00208ff6, 0x00000000, 0x00000001, 0x0a000038, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000, + 0x00004002, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x05000036, 0x00102032, 0x00000002, + 0x00100086, 0x00000000, 0x05000036, 0x00102032, 0x00000003, 0x001005d6, 0x00000000, 0x05000036, + 0x00100042, 0x00000001, 0x00004001, 0x3f800000, 0x08000010, 0x00100012, 0x00000000, 0x00208246, + 0x00000000, 0x00000002, 0x00100246, 0x00000001, 0x08000010, 0x00100022, 0x00000000, 0x00208246, + 0x00000000, 0x00000003, 0x00100246, 0x00000001, 0x08000038, 0x00100022, 0x00000001, 0x0010001a, + 0x00000000, 0x0020803a, 0x00000000, 0x00000003, 0x08000038, 0x00100012, 0x00000001, 0x0010000a, + 0x00000000, 0x0020803a, 0x00000000, 0x00000002, 0x0a000000, 0x00102032, 0x00000004, 0x00100046, + 0x00000001, 0x00004002, 0xbf800000, 0x3f800000, 0x00000000, 0x00000000, 0x08000036, 0x001020c2, + 0x00000004, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x3f800000, 0x0100003e, + }; + /* ⎡p0.x p0.y 1⎤ + * A = ⎢p1.x p1.y 1⎥ + * ⎣p2.x p2.y 1⎦ + * + * ⎡1 0⎤ + * B = ⎢1 1⎥ + * ⎣0 1⎦ + * + * A' = ⎡p1.x-p0.x p1.y-p0.y⎤ + * ⎣p2.x-p0.x p2.y-p0.y⎦ + * + * B' = ⎡ 0 1⎤ + * ⎣-1 1⎦ + * + * A'T = B' + * T = A'⁻¹B' = (B'⁻¹A')⁻¹ + */ + static const DWORD vs_code_arc_outline[] = + { +#if 0 + float3x2 transform_geometry; + float stroke_width; + float4 transform_rtx; + float4 transform_rty; + + struct output + { + float2 p : WORLD_POSITION; + float4 b : BEZIER; + nointerpolation float2x2 stroke_transform : STROKE_TRANSFORM; + float4 position : SV_POSITION; + }; + + void main(float2 position : POSITION, float2 p0 : P0, float2 p1 : P1, float2 p2 : P2, + float2 prev : PREV, float2 next : NEXT, out struct output o) + { + float2 q_prev, q_next, v_p, q_i, p; + float2x2 geom, rt, p_inv; + float l; + float a; + float2 bc; + + geom = float2x2(transform_geometry._11_21, transform_geometry._12_22); + rt = float2x2(transform_rtx.xy, transform_rty.xy); + o.stroke_transform = rt * stroke_width * 0.5f; + + p = mul(geom, position); + p0 = mul(geom, p0); + p1 = mul(geom, p1); + p2 = mul(geom, p2); + + p -= p0; + p1 -= p0; + p2 -= p0; + + q_prev = normalize(mul(geom, prev)); + q_next = normalize(mul(geom, next)); + + v_p = float2(-q_prev.y, q_prev.x); + l = -dot(v_p, q_next) / (1.0f + dot(q_prev, q_next)); + q_i = l * q_prev + v_p; + p += 0.5f * stroke_width * q_i; + + p_inv = float2x2(p1.y, -p1.x, p2.y - p1.y, p1.x - p2.x) / (p1.x * p2.y - p2.x * p1.y); + o.b.xy = mul(p_inv, p) + float2(1.0f, 0.0f); + o.b.zw = 0.0f; + + o.p = mul(float3(position, 1.0f), transform_geometry) + 0.5f * stroke_width * q_i; + position = mul(float2x3(transform_rtx.xyz, transform_rty.xyz), float3(o.p, 1.0f)) + * float2(transform_rtx.w, transform_rty.w); + o.position = float4(position + float2(-1.0f, 1.0f), 0.0f, 1.0f); + } +#endif + 0x43425844, 0xde1911bf, 0xfff8c893, 0xb0bfc24d, 0x78c9bbc4, 0x00000001, 0x00000924, 0x00000003, + 0x0000002c, 0x000000e4, 0x000001a0, 0x4e475349, 0x000000b0, 0x00000006, 0x00000008, 0x00000098, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000303, 0x000000a1, 0x00000000, 0x00000000, + 0x00000003, 0x00000001, 0x00000303, 0x000000a1, 0x00000001, 0x00000000, 0x00000003, 0x00000002, + 0x00000303, 0x000000a1, 0x00000002, 0x00000000, 0x00000003, 0x00000003, 0x00000303, 0x000000a3, + 0x00000000, 0x00000000, 0x00000003, 0x00000004, 0x00000303, 0x000000a8, 0x00000000, 0x00000000, + 0x00000003, 0x00000005, 0x00000303, 0x49534f50, 0x4e4f4954, 0x50005000, 0x00564552, 0x5458454e, + 0xababab00, 0x4e47534f, 0x000000b4, 0x00000005, 0x00000008, 0x00000080, 0x00000000, 0x00000000, + 0x00000003, 0x00000000, 0x00000c03, 0x0000008f, 0x00000000, 0x00000000, 0x00000003, 0x00000001, + 0x0000000f, 0x00000096, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000c03, 0x00000096, + 0x00000001, 0x00000000, 0x00000003, 0x00000003, 0x00000c03, 0x000000a7, 0x00000000, 0x00000001, + 0x00000003, 0x00000004, 0x0000000f, 0x4c524f57, 0x4f505f44, 0x49544953, 0x42004e4f, 0x45495a45, + 0x54530052, 0x454b4f52, 0x4152545f, 0x4f46534e, 0x53004d52, 0x4f505f56, 0x49544953, 0xab004e4f, + 0x52444853, 0x0000077c, 0x00010040, 0x000001df, 0x04000059, 0x00208e46, 0x00000000, 0x00000004, + 0x0300005f, 0x00101032, 0x00000000, 0x0300005f, 0x00101032, 0x00000001, 0x0300005f, 0x00101032, + 0x00000002, 0x0300005f, 0x00101032, 0x00000003, 0x0300005f, 0x00101032, 0x00000004, 0x0300005f, + 0x00101032, 0x00000005, 0x03000065, 0x00102032, 0x00000000, 0x03000065, 0x001020f2, 0x00000001, + 0x03000065, 0x00102032, 0x00000002, 0x03000065, 0x00102032, 0x00000003, 0x04000067, 0x001020f2, + 0x00000004, 0x00000001, 0x02000068, 0x00000004, 0x0800000f, 0x00100012, 0x00000000, 0x00208046, + 0x00000000, 0x00000000, 0x00101046, 0x00000005, 0x0800000f, 0x00100022, 0x00000000, 0x00208046, + 0x00000000, 0x00000001, 0x00101046, 0x00000005, 0x0700000f, 0x00100042, 0x00000000, 0x00100046, + 0x00000000, 0x00100046, 0x00000000, 0x05000044, 0x00100042, 0x00000000, 0x0010002a, 0x00000000, + 0x07000038, 0x00100032, 0x00000000, 0x00100aa6, 0x00000000, 0x00100046, 0x00000000, 0x0800000f, + 0x00100012, 0x00000001, 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000004, 0x0800000f, + 0x00100022, 0x00000001, 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000004, 0x0700000f, + 0x00100042, 0x00000000, 0x00100046, 0x00000001, 0x00100046, 0x00000001, 0x05000044, 0x00100042, + 0x00000000, 0x0010002a, 0x00000000, 0x07000038, 0x00100032, 0x00000001, 0x00100aa6, 0x00000000, + 0x00100046, 0x00000001, 0x06000036, 0x001000c2, 0x00000001, 0x80100556, 0x00000041, 0x00000001, + 0x0700000f, 0x00100042, 0x00000000, 0x00100a26, 0x00000001, 0x00100046, 0x00000000, 0x0700000f, + 0x00100012, 0x00000000, 0x00100046, 0x00000001, 0x00100046, 0x00000000, 0x07000000, 0x00100012, + 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000, 0x0800000e, 0x00100012, 0x00000000, + 0x8010002a, 0x00000041, 0x00000000, 0x0010000a, 0x00000000, 0x09000032, 0x00100032, 0x00000000, + 0x00100006, 0x00000000, 0x00100046, 0x00000001, 0x00100f36, 0x00000001, 0x05000036, 0x00100032, + 0x00000001, 0x00101046, 0x00000000, 0x05000036, 0x00100042, 0x00000001, 0x00004001, 0x3f800000, + 0x08000010, 0x00100012, 0x00000002, 0x00100246, 0x00000001, 0x00208246, 0x00000000, 0x00000000, + 0x08000010, 0x00100022, 0x00000002, 0x00100246, 0x00000001, 0x00208246, 0x00000000, 0x00000001, + 0x08000038, 0x00100042, 0x00000000, 0x0020803a, 0x00000000, 0x00000001, 0x00004001, 0x3f000000, + 0x09000032, 0x00100032, 0x00000001, 0x00100aa6, 0x00000000, 0x00100046, 0x00000000, 0x00100046, + 0x00000002, 0x05000036, 0x00102032, 0x00000000, 0x00100046, 0x00000001, 0x0800000f, 0x00100012, + 0x00000002, 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000000, 0x0800000f, 0x00100022, + 0x00000002, 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000000, 0x0800000f, 0x00100022, + 0x00000003, 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000001, 0x0800000f, 0x00100012, + 0x00000003, 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000001, 0x08000000, 0x00100032, + 0x00000002, 0x00100046, 0x00000002, 0x80100516, 0x00000041, 0x00000003, 0x09000032, 0x00100032, + 0x00000000, 0x00100aa6, 0x00000000, 0x00100046, 0x00000000, 0x00100046, 0x00000002, 0x0800000f, + 0x00100022, 0x00000002, 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000003, 0x0800000f, + 0x00100012, 0x00000002, 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000003, 0x08000000, + 0x001000c2, 0x00000000, 0x80100406, 0x00000041, 0x00000003, 0x00100406, 0x00000002, 0x0800000f, + 0x00100022, 0x00000002, 0x00208046, 0x00000000, 0x00000000, 0x00101046, 0x00000002, 0x0800000f, + 0x00100012, 0x00000002, 0x00208046, 0x00000000, 0x00000001, 0x00101046, 0x00000002, 0x08000000, + 0x00100032, 0x00000002, 0x80100046, 0x00000041, 0x00000003, 0x00100046, 0x00000002, 0x07000038, + 0x00100082, 0x00000001, 0x0010003a, 0x00000000, 0x0010000a, 0x00000002, 0x0a000032, 0x00100082, + 0x00000001, 0x0010001a, 0x00000002, 0x0010002a, 0x00000000, 0x8010003a, 0x00000041, 0x00000001, + 0x08000000, 0x00100042, 0x00000003, 0x0010002a, 0x00000000, 0x8010000a, 0x00000041, 0x00000002, + 0x08000000, 0x00100082, 0x00000003, 0x8010003a, 0x00000041, 0x00000000, 0x0010001a, 0x00000002, + 0x0a000038, 0x00100032, 0x00000003, 0x00100046, 0x00000002, 0x00004002, 0x3f800000, 0xbf800000, + 0x00000000, 0x00000000, 0x0700000e, 0x001000f2, 0x00000002, 0x00100e46, 0x00000003, 0x00100ff6, + 0x00000001, 0x0700000f, 0x00100012, 0x00000002, 0x00100046, 0x00000002, 0x00100046, 0x00000000, + 0x0700000f, 0x00100022, 0x00000002, 0x00100ae6, 0x00000002, 0x00100046, 0x00000000, 0x0a000000, + 0x00102032, 0x00000001, 0x00100046, 0x00000002, 0x00004002, 0x3f800000, 0x00000000, 0x00000000, + 0x00000000, 0x08000036, 0x001020c2, 0x00000001, 0x00004002, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x06000036, 0x00100032, 0x00000000, 0x00208046, 0x00000000, 0x00000002, 0x06000036, + 0x001000c2, 0x00000000, 0x00208406, 0x00000000, 0x00000003, 0x08000038, 0x001000f2, 0x00000000, + 0x00100e46, 0x00000000, 0x00208ff6, 0x00000000, 0x00000001, 0x0a000038, 0x001000f2, 0x00000000, + 0x00100e46, 0x00000000, 0x00004002, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x05000036, + 0x00102032, 0x00000002, 0x00100086, 0x00000000, 0x05000036, 0x00102032, 0x00000003, 0x001005d6, + 0x00000000, 0x05000036, 0x00100042, 0x00000001, 0x00004001, 0x3f800000, 0x08000010, 0x00100012, + 0x00000000, 0x00208246, 0x00000000, 0x00000002, 0x00100246, 0x00000001, 0x08000010, 0x00100022, + 0x00000000, 0x00208246, 0x00000000, 0x00000003, 0x00100246, 0x00000001, 0x08000038, 0x00100022, + 0x00000001, 0x0010001a, 0x00000000, 0x0020803a, 0x00000000, 0x00000003, 0x08000038, 0x00100012, + 0x00000001, 0x0010000a, 0x00000000, 0x0020803a, 0x00000000, 0x00000002, 0x0a000000, 0x00102032, + 0x00000004, 0x00100046, 0x00000001, 0x00004002, 0xbf800000, 0x3f800000, 0x00000000, 0x00000000, + 0x08000036, 0x001020c2, 0x00000004, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x3f800000, + 0x0100003e, + }; + static const DWORD vs_code_triangle[] = + { +#if 0 + float3x2 transform_geometry; + float4 transform_rtx; + float4 transform_rty; + + struct output + { + float2 p : WORLD_POSITION; + float4 b : BEZIER; + nointerpolation float2x2 stroke_transform : STROKE_TRANSFORM; + float4 position : SV_POSITION; + }; + + void main(float2 position : POSITION, out struct output o) + { + o.p = mul(float3(position, 1.0f), transform_geometry); + o.b = float4(1.0, 0.0, 1.0, 1.0); + o.stroke_transform = float2x2(1.0, 0.0, 0.0, 1.0); + position = mul(float2x3(transform_rtx.xyz, transform_rty.xyz), float3(o.p, 1.0f)) + * float2(transform_rtx.w, transform_rty.w); + o.position = float4(position + float2(-1.0f, 1.0f), 0.0f, 1.0f); + } +#endif + 0x43425844, 0xda43bf17, 0x06e6d155, 0xdbce2ae5, 0x8aed6fd8, 0x00000001, 0x0000034c, 0x00000003, + 0x0000002c, 0x00000060, 0x0000011c, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000303, 0x49534f50, 0x4e4f4954, 0xababab00, + 0x4e47534f, 0x000000b4, 0x00000005, 0x00000008, 0x00000080, 0x00000000, 0x00000000, 0x00000003, + 0x00000000, 0x00000c03, 0x0000008f, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f, + 0x00000096, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000c03, 0x00000096, 0x00000001, + 0x00000000, 0x00000003, 0x00000003, 0x00000c03, 0x000000a7, 0x00000000, 0x00000001, 0x00000003, + 0x00000004, 0x0000000f, 0x4c524f57, 0x4f505f44, 0x49544953, 0x42004e4f, 0x45495a45, 0x54530052, + 0x454b4f52, 0x4152545f, 0x4f46534e, 0x53004d52, 0x4f505f56, 0x49544953, 0xab004e4f, 0x52444853, + 0x00000228, 0x00010040, 0x0000008a, 0x04000059, 0x00208e46, 0x00000000, 0x00000004, 0x0300005f, + 0x00101032, 0x00000000, 0x03000065, 0x00102032, 0x00000000, 0x03000065, 0x001020f2, 0x00000001, + 0x03000065, 0x00102032, 0x00000002, 0x03000065, 0x00102032, 0x00000003, 0x04000067, 0x001020f2, + 0x00000004, 0x00000001, 0x02000068, 0x00000002, 0x05000036, 0x00100032, 0x00000000, 0x00101046, + 0x00000000, 0x05000036, 0x00100042, 0x00000000, 0x00004001, 0x3f800000, 0x08000010, 0x00100012, + 0x00000001, 0x00100246, 0x00000000, 0x00208246, 0x00000000, 0x00000000, 0x08000010, 0x00100022, + 0x00000001, 0x00100246, 0x00000000, 0x00208246, 0x00000000, 0x00000001, 0x05000036, 0x00102032, + 0x00000000, 0x00100046, 0x00000001, 0x08000036, 0x001020f2, 0x00000001, 0x00004002, 0x3f800000, + 0x00000000, 0x3f800000, 0x3f800000, 0x08000036, 0x00102032, 0x00000002, 0x00004002, 0x3f800000, + 0x00000000, 0x00000000, 0x00000000, 0x08000036, 0x00102032, 0x00000003, 0x00004002, 0x00000000, + 0x3f800000, 0x00000000, 0x00000000, 0x05000036, 0x00100042, 0x00000001, 0x00004001, 0x3f800000, + 0x08000010, 0x00100012, 0x00000000, 0x00208246, 0x00000000, 0x00000002, 0x00100246, 0x00000001, + 0x08000010, 0x00100022, 0x00000000, 0x00208246, 0x00000000, 0x00000003, 0x00100246, 0x00000001, + 0x08000038, 0x00100022, 0x00000001, 0x0010001a, 0x00000000, 0x0020803a, 0x00000000, 0x00000003, + 0x08000038, 0x00100012, 0x00000001, 0x0010000a, 0x00000000, 0x0020803a, 0x00000000, 0x00000002, + 0x0a000000, 0x00102032, 0x00000004, 0x00100046, 0x00000001, 0x00004002, 0xbf800000, 0x3f800000, + 0x00000000, 0x00000000, 0x08000036, 0x001020c2, 0x00000004, 0x00004002, 0x00000000, 0x00000000, + 0x00000000, 0x3f800000, 0x0100003e, + }; + static const DWORD vs_code_curve[] = + { +#if 0 + float3x2 transform_geometry; + float4 transform_rtx; + float4 transform_rty; + + struct output + { + float2 p : WORLD_POSITION; + float4 b : BEZIER; + nointerpolation float2x2 stroke_transform : STROKE_TRANSFORM; + float4 position : SV_POSITION; + }; + + void main(float2 position : POSITION, float3 texcoord : TEXCOORD0, out struct output o) + { + o.p = mul(float3(position, 1.0f), transform_geometry); + o.b = float4(texcoord, 1.0); + o.stroke_transform = float2x2(1.0, 0.0, 0.0, 1.0); + position = mul(float2x3(transform_rtx.xyz, transform_rty.xyz), float3(o.p, 1.0f)) + * float2(transform_rtx.w, transform_rty.w); + o.position = float4(position + float2(-1.0f, 1.0f), 0.0f, 1.0f); + } +#endif + 0x43425844, 0xedb7472a, 0x2c2ea147, 0x36710079, 0xffc2e907, 0x00000001, 0x00000380, 0x00000003, + 0x0000002c, 0x00000080, 0x0000013c, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000303, 0x00000041, 0x00000000, 0x00000000, + 0x00000003, 0x00000001, 0x00000707, 0x49534f50, 0x4e4f4954, 0x58455400, 0x524f4f43, 0xabab0044, + 0x4e47534f, 0x000000b4, 0x00000005, 0x00000008, 0x00000080, 0x00000000, 0x00000000, 0x00000003, + 0x00000000, 0x00000c03, 0x0000008f, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f, + 0x00000096, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000c03, 0x00000096, 0x00000001, + 0x00000000, 0x00000003, 0x00000003, 0x00000c03, 0x000000a7, 0x00000000, 0x00000001, 0x00000003, + 0x00000004, 0x0000000f, 0x4c524f57, 0x4f505f44, 0x49544953, 0x42004e4f, 0x45495a45, 0x54530052, + 0x454b4f52, 0x4152545f, 0x4f46534e, 0x53004d52, 0x4f505f56, 0x49544953, 0xab004e4f, 0x52444853, + 0x0000023c, 0x00010040, 0x0000008f, 0x04000059, 0x00208e46, 0x00000000, 0x00000004, 0x0300005f, + 0x00101032, 0x00000000, 0x0300005f, 0x00101072, 0x00000001, 0x03000065, 0x00102032, 0x00000000, + 0x03000065, 0x001020f2, 0x00000001, 0x03000065, 0x00102032, 0x00000002, 0x03000065, 0x00102032, + 0x00000003, 0x04000067, 0x001020f2, 0x00000004, 0x00000001, 0x02000068, 0x00000002, 0x05000036, + 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x05000036, 0x00100042, 0x00000000, 0x00004001, + 0x3f800000, 0x08000010, 0x00100012, 0x00000001, 0x00100246, 0x00000000, 0x00208246, 0x00000000, + 0x00000000, 0x08000010, 0x00100022, 0x00000001, 0x00100246, 0x00000000, 0x00208246, 0x00000000, + 0x00000001, 0x05000036, 0x00102032, 0x00000000, 0x00100046, 0x00000001, 0x05000036, 0x00102072, + 0x00000001, 0x00101246, 0x00000001, 0x05000036, 0x00102082, 0x00000001, 0x00004001, 0x3f800000, + 0x08000036, 0x00102032, 0x00000002, 0x00004002, 0x3f800000, 0x00000000, 0x00000000, 0x00000000, + 0x08000036, 0x00102032, 0x00000003, 0x00004002, 0x00000000, 0x3f800000, 0x00000000, 0x00000000, + 0x05000036, 0x00100042, 0x00000001, 0x00004001, 0x3f800000, 0x08000010, 0x00100012, 0x00000000, + 0x00208246, 0x00000000, 0x00000002, 0x00100246, 0x00000001, 0x08000010, 0x00100022, 0x00000000, + 0x00208246, 0x00000000, 0x00000003, 0x00100246, 0x00000001, 0x08000038, 0x00100022, 0x00000001, + 0x0010001a, 0x00000000, 0x0020803a, 0x00000000, 0x00000003, 0x08000038, 0x00100012, 0x00000001, + 0x0010000a, 0x00000000, 0x0020803a, 0x00000000, 0x00000002, 0x0a000000, 0x00102032, 0x00000004, + 0x00100046, 0x00000001, 0x00004002, 0xbf800000, 0x3f800000, 0x00000000, 0x00000000, 0x08000036, + 0x001020c2, 0x00000004, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x3f800000, 0x0100003e, + }; + static const DWORD ps_code[] = + { +#if 0 +#define BRUSH_TYPE_SOLID 0 +#define BRUSH_TYPE_LINEAR 1 +#define BRUSH_TYPE_RADIAL 2 +#define BRUSH_TYPE_BITMAP 3 +#define BRUSH_TYPE_COUNT 4 + + bool outline; + bool is_arc; + struct brush + { + uint type; + float opacity; + float4 data[3]; + } colour_brush, opacity_brush; + + SamplerState s0, s1; + Texture2D t0, t1; + Buffer<float4> b0, b1; + + struct input + { + float2 p : WORLD_POSITION; + float4 b : BEZIER; + nointerpolation float2x2 stroke_transform : STROKE_TRANSFORM; + }; + + float4 sample_gradient(Buffer<float4> gradient, uint stop_count, float position) + { + float4 c_low, c_high; + float p_low, p_high; + uint i; + + p_low = gradient.Load(0).x; + c_low = gradient.Load(1); + c_high = c_low; + + if (position < p_low) + return c_low; + + for (i = 1; i < stop_count; ++i) + { + p_high = gradient.Load(i * 2).x; + c_high = gradient.Load(i * 2 + 1); + + if (position >= p_low && position <= p_high) + return lerp(c_low, c_high, (position - p_low) / (p_high - p_low)); + + p_low = p_high; + c_low = c_high; + } + + return c_high; + } + + float4 brush_linear(struct brush brush, Buffer<float4> gradient, float2 position) + { + float2 start, end, v_p, v_q; + uint stop_count; + float p; + + 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); + + return sample_gradient(gradient, stop_count, p); + } + + float4 brush_radial(struct brush brush, Buffer<float4> gradient, float2 position) + { + float2 centre, offset, ra, rb, v_p, v_q, r; + float b, c, l, t; + uint stop_count; + + centre = brush.data[0].xy; + offset = brush.data[0].zw; + ra = brush.data[1].xy; + rb = brush.data[1].zw; + stop_count = asuint(brush.data[2].x); + + /* Project onto ra, rb. */ + r = float2(dot(ra, ra), dot(rb, rb)); + v_p = position - (centre + offset); + v_p = float2(dot(v_p, ra), dot(v_p, rb)) / r; + v_q = float2(dot(offset, ra), dot(offset, rb)) / r; + + /* ‖t·p̂ + q⃑‖ = 1 + * (t·p̂ + q⃑) · (t·p̂ + q⃑) = 1 + * t² + 2·(p̂·q⃑)·t + (q⃑·q⃑) = 1 + * + * b = p̂·q⃑ + * c = q⃑·q⃑ - 1 + * t = -b + √(b² - c) */ + l = length(v_p); + b = dot(v_p, v_q) / l; + c = dot(v_q, v_q) - 1.0; + t = -b + sqrt(b * b - c); + + return sample_gradient(gradient, stop_count, l / t); + } + + float4 brush_bitmap(struct brush brush, Texture2D t, SamplerState s, float2 position) + { + float3 transform[2]; + bool ignore_alpha; + float2 texcoord; + float4 colour; + + transform[0] = brush.data[0].xyz; + transform[1] = brush.data[1].xyz; + ignore_alpha = asuint(brush.data[1].w); + + texcoord.x = dot(position.xy, transform[0].xy) + transform[0].z; + texcoord.y = dot(position.xy, transform[1].xy) + transform[1].z; + colour = t.Sample(s, texcoord); + if (ignore_alpha) + colour.a = 1.0; + return colour; + } + + 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_RADIAL) + return brush_radial(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); + } + + float4 main(struct input i) : SV_Target + { + float4 colour; + + colour = sample_brush(colour_brush, t0, s0, b0, i.p); + if (opacity_brush.type < BRUSH_TYPE_COUNT) + colour *= sample_brush(opacity_brush, t1, s1, b1, i.p).a; + + if (outline) + { + float2 du, dv, df; + float4 uv; + + /* Evaluate the implicit form of the curve (u² - v = 0 + * for Béziers, u² + v² - 1 = 0 for arcs) in texture + * space, using the screen-space partial derivatives + * to convert the calculated distance to object space. + * + * d(x, y) = |f(x, y)| / ‖∇f(x, y)‖ + * = |f(x, y)| / √((∂f/∂x)² + (∂f/∂y)²) + * + * For Béziers: + * f(x, y) = u(x, y)² - v(x, y) + * ∂f/∂x = 2u · ∂u/∂x - ∂v/∂x + * ∂f/∂y = 2u · ∂u/∂y - ∂v/∂y + * + * For arcs: + * f(x, y) = u(x, y)² + v(x, y)² - 1 + * ∂f/∂x = 2u · ∂u/∂x + 2v · ∂v/∂x + * ∂f/∂y = 2u · ∂u/∂y + 2v · ∂v/∂y */ + uv = i.b; + du = float2(ddx(uv.x), ddy(uv.x)); + dv = float2(ddx(uv.y), ddy(uv.y)); + + if (!is_arc) + { + df = 2.0f * uv.x * du - dv; + + clip(dot(df, uv.zw)); + clip(length(mul(i.stroke_transform, df)) - abs(uv.x * uv.x - uv.y)); + } + else + { + df = 2.0f * uv.x * du + 2.0f * uv.y * dv; + + clip(dot(df, uv.zw)); + clip(length(mul(i.stroke_transform, df)) - abs(uv.x * uv.x + uv.y * uv.y - 1.0f)); + } + } + else + { + /* Evaluate the implicit form of the curve in texture space. + * "i.b.z" determines which side of the curve is shaded. */ + if (!is_arc) + { + clip((i.b.x * i.b.x - i.b.y) * i.b.z); + } + else + { + clip((i.b.x * i.b.x + i.b.y * i.b.y - 1.0) * i.b.z); + } + } + + return colour; + } +#endif + 0x43425844, 0xa8fee730, 0x92fa2196, 0xaf9f3eff, 0x888d4048, 0x00000001, 0x00002000, 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, 0x00001f00, + 0x00000040, 0x000007c0, 0x04000059, 0x00208e46, 0x00000000, 0x00000009, 0x0300005a, 0x00106000, + 0x00000000, 0x0300005a, 0x00106000, 0x00000001, 0x04001858, 0x00107000, 0x00000000, 0x00005555, + 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, 0x0000000a, 0x09000038, 0x001000f2, 0x00000000, + 0x00208556, 0x00000000, 0x00000001, 0x00208e46, 0x00000000, 0x00000002, 0x0404001f, 0x0020800a, + 0x00000000, 0x00000001, 0x08000020, 0x00100012, 0x00000001, 0x0020800a, 0x00000000, 0x00000001, + 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, 0x0900000f, 0x00100012, 0x00000002, 0x00208046, + 0x00000000, 0x00000003, 0x00208046, 0x00000000, 0x00000003, 0x0900000f, 0x00100022, 0x00000002, + 0x00208ae6, 0x00000000, 0x00000003, 0x00208ae6, 0x00000000, 0x00000003, 0x09000000, 0x00100062, + 0x00000001, 0x00208ba6, 0x00000000, 0x00000002, 0x00208106, 0x00000000, 0x00000002, 0x08000000, + 0x00100062, 0x00000001, 0x80100656, 0x00000041, 0x00000001, 0x00101106, 0x00000000, 0x0800000f, + 0x00100012, 0x00000003, 0x00100596, 0x00000001, 0x00208046, 0x00000000, 0x00000003, 0x0800000f, + 0x00100022, 0x00000003, 0x00100596, 0x00000001, 0x00208ae6, 0x00000000, 0x00000003, 0x0700000e, + 0x00100062, 0x00000001, 0x00100106, 0x00000003, 0x00100106, 0x00000002, 0x0900000f, 0x00100012, + 0x00000003, 0x00208ae6, 0x00000000, 0x00000002, 0x00208046, 0x00000000, 0x00000003, 0x0900000f, + 0x00100022, 0x00000003, 0x00208ae6, 0x00000000, 0x00000002, 0x00208ae6, 0x00000000, 0x00000003, + 0x0700000e, 0x00100032, 0x00000002, 0x00100046, 0x00000003, 0x00100046, 0x00000002, 0x0700000f, + 0x00100082, 0x00000001, 0x00100596, 0x00000001, 0x00100596, 0x00000001, 0x0500004b, 0x00100082, + 0x00000001, 0x0010003a, 0x00000001, 0x0700000f, 0x00100022, 0x00000001, 0x00100596, 0x00000001, + 0x00100046, 0x00000002, 0x0700000e, 0x00100022, 0x00000001, 0x0010001a, 0x00000001, 0x0010003a, + 0x00000001, 0x0700000f, 0x00100042, 0x00000001, 0x00100046, 0x00000002, 0x00100046, 0x00000002, + 0x07000000, 0x00100042, 0x00000001, 0x0010002a, 0x00000001, 0x00004001, 0xbf800000, 0x0a000032, + 0x00100042, 0x00000001, 0x0010001a, 0x00000001, 0x0010001a, 0x00000001, 0x8010002a, 0x00000041, + 0x00000001, 0x0500004b, 0x00100042, 0x00000001, 0x0010002a, 0x00000001, 0x08000000, 0x00100022, + 0x00000001, 0x0010002a, 0x00000001, 0x8010001a, 0x00000041, 0x00000001, 0x0700000e, 0x00100022, + 0x00000001, 0x0010003a, 0x00000001, 0x0010001a, 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, 0x00000004, 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, + 0x00000003, 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, 0x01000015, 0x0800004f, + 0x00100012, 0x00000001, 0x0020800a, 0x00000000, 0x00000005, 0x00004001, 0x00000004, 0x0304001f, + 0x0010000a, 0x00000001, 0x09000038, 0x00100012, 0x00000001, 0x0020801a, 0x00000000, 0x00000005, + 0x0020803a, 0x00000000, 0x00000006, 0x0404001f, 0x0020800a, 0x00000000, 0x00000005, 0x08000020, + 0x00100022, 0x00000001, 0x0020800a, 0x00000000, 0x00000005, 0x00004001, 0x00000001, 0x0304001f, + 0x0010001a, 0x00000001, 0x09000000, 0x001000c2, 0x00000001, 0x00101406, 0x00000000, 0x80208406, + 0x00000041, 0x00000000, 0x00000006, 0x0a000000, 0x00100032, 0x00000002, 0x80208046, 0x00000041, + 0x00000000, 0x00000006, 0x00208ae6, 0x00000000, 0x00000006, 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, 0x00100062, 0x00000002, + 0x00100ff6, 0x00000003, 0x05000036, 0x00100082, 0x00000002, 0x0010000a, 0x00000002, 0x08000036, + 0x00100032, 0x00000003, 0x00004002, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x01000030, + 0x08000050, 0x00100042, 0x00000003, 0x0010000a, 0x00000003, 0x0020800a, 0x00000000, 0x00000007, + 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, 0x0010003a, 0x00000002, 0x0700001d, 0x00100022, 0x00000004, 0x0010000a, 0x00000004, + 0x0010002a, 0x00000001, 0x07000001, 0x00100042, 0x00000003, 0x0010002a, 0x00000003, 0x0010001a, + 0x00000004, 0x0304001f, 0x0010002a, 0x00000003, 0x08000000, 0x00100022, 0x00000004, 0x0010002a, + 0x00000001, 0x8010003a, 0x00000041, 0x00000002, 0x08000000, 0x00100042, 0x00000004, 0x8010003a, + 0x00000041, 0x00000002, 0x0010000a, 0x00000004, 0x0700000e, 0x00100022, 0x00000004, 0x0010001a, + 0x00000004, 0x0010002a, 0x00000004, 0x08000000, 0x00100042, 0x00000004, 0x8010001a, 0x00000041, + 0x00000002, 0x0010003a, 0x00000005, 0x09000032, 0x00100022, 0x00000004, 0x0010001a, 0x00000004, + 0x0010002a, 0x00000004, 0x0010001a, 0x00000002, 0x05000036, 0x00100042, 0x00000002, 0x0010003a, + 0x00000005, 0x05000036, 0x00100022, 0x00000003, 0x00004001, 0xffffffff, 0x05000036, 0x00100082, + 0x00000001, 0x0010001a, 0x00000004, 0x01000002, 0x01000015, 0x05000036, 0x00100022, 0x00000002, + 0x0010003a, 0x00000005, 0x05000036, 0x00100082, 0x00000002, 0x0010000a, 0x00000004, 0x0700001e, + 0x00100012, 0x00000003, 0x0010000a, 0x00000003, 0x00004001, 0x00000001, 0x05000036, 0x00100042, + 0x00000002, 0x0010003a, 0x00000005, 0x05000036, 0x00100032, 0x00000003, 0x00100086, 0x00000003, + 0x01000016, 0x09000037, 0x00100042, 0x00000001, 0x0010001a, 0x00000003, 0x0010003a, 0x00000001, + 0x0010002a, 0x00000002, 0x01000012, 0x05000036, 0x00100042, 0x00000001, 0x0010003a, 0x00000003, + 0x01000015, 0x08000038, 0x00100012, 0x00000001, 0x0010002a, 0x00000001, 0x0020801a, 0x00000000, + 0x00000005, 0x01000015, 0x0300001f, 0x0010001a, 0x00000001, 0x08000020, 0x00100022, 0x00000001, + 0x0020800a, 0x00000000, 0x00000005, 0x00004001, 0x00000002, 0x0304001f, 0x0010001a, 0x00000001, + 0x0900000f, 0x00100012, 0x00000002, 0x00208046, 0x00000000, 0x00000007, 0x00208046, 0x00000000, + 0x00000007, 0x0900000f, 0x00100022, 0x00000002, 0x00208ae6, 0x00000000, 0x00000007, 0x00208ae6, + 0x00000000, 0x00000007, 0x09000000, 0x001000c2, 0x00000001, 0x00208ea6, 0x00000000, 0x00000006, + 0x00208406, 0x00000000, 0x00000006, 0x08000000, 0x001000c2, 0x00000001, 0x80100ea6, 0x00000041, + 0x00000001, 0x00101406, 0x00000000, 0x0800000f, 0x00100012, 0x00000003, 0x00100ae6, 0x00000001, + 0x00208046, 0x00000000, 0x00000007, 0x0800000f, 0x00100022, 0x00000003, 0x00100ae6, 0x00000001, + 0x00208ae6, 0x00000000, 0x00000007, 0x0700000e, 0x001000c2, 0x00000001, 0x00100406, 0x00000003, + 0x00100406, 0x00000002, 0x0900000f, 0x00100012, 0x00000003, 0x00208ae6, 0x00000000, 0x00000006, + 0x00208046, 0x00000000, 0x00000007, 0x0900000f, 0x00100022, 0x00000003, 0x00208ae6, 0x00000000, + 0x00000006, 0x00208ae6, 0x00000000, 0x00000007, 0x0700000e, 0x00100032, 0x00000002, 0x00100046, + 0x00000003, 0x00100046, 0x00000002, 0x0700000f, 0x00100042, 0x00000002, 0x00100ae6, 0x00000001, + 0x00100ae6, 0x00000001, 0x0500004b, 0x00100042, 0x00000002, 0x0010002a, 0x00000002, 0x0700000f, + 0x00100042, 0x00000001, 0x00100ae6, 0x00000001, 0x00100046, 0x00000002, 0x0700000e, 0x00100042, + 0x00000001, 0x0010002a, 0x00000001, 0x0010002a, 0x00000002, 0x0700000f, 0x00100082, 0x00000001, + 0x00100046, 0x00000002, 0x00100046, 0x00000002, 0x07000000, 0x00100082, 0x00000001, 0x0010003a, + 0x00000001, 0x00004001, 0xbf800000, 0x0a000032, 0x00100082, 0x00000001, 0x0010002a, 0x00000001, + 0x0010002a, 0x00000001, 0x8010003a, 0x00000041, 0x00000001, 0x0500004b, 0x00100082, 0x00000001, + 0x0010003a, 0x00000001, 0x08000000, 0x00100042, 0x00000001, 0x0010003a, 0x00000001, 0x8010002a, + 0x00000041, 0x00000001, 0x0700000e, 0x00100042, 0x00000001, 0x0010002a, 0x00000002, 0x0010002a, + 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, 0x00100062, 0x00000002, 0x00100ff6, + 0x00000003, 0x05000036, 0x00100082, 0x00000002, 0x0010000a, 0x00000002, 0x08000036, 0x00100032, + 0x00000003, 0x00004002, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x01000030, 0x08000050, + 0x00100042, 0x00000003, 0x0010000a, 0x00000003, 0x0020800a, 0x00000000, 0x00000008, 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, + 0x0010003a, 0x00000002, 0x0700001d, 0x00100022, 0x00000004, 0x0010000a, 0x00000004, 0x0010002a, + 0x00000001, 0x07000001, 0x00100042, 0x00000003, 0x0010002a, 0x00000003, 0x0010001a, 0x00000004, + 0x0304001f, 0x0010002a, 0x00000003, 0x08000000, 0x00100022, 0x00000004, 0x0010002a, 0x00000001, + 0x8010003a, 0x00000041, 0x00000002, 0x08000000, 0x00100042, 0x00000004, 0x8010003a, 0x00000041, + 0x00000002, 0x0010000a, 0x00000004, 0x0700000e, 0x00100022, 0x00000004, 0x0010001a, 0x00000004, + 0x0010002a, 0x00000004, 0x08000000, 0x00100042, 0x00000004, 0x8010001a, 0x00000041, 0x00000002, + 0x0010003a, 0x00000005, 0x09000032, 0x00100022, 0x00000004, 0x0010001a, 0x00000004, 0x0010002a, + 0x00000004, 0x0010001a, 0x00000002, 0x05000036, 0x00100042, 0x00000002, 0x0010003a, 0x00000005, + 0x05000036, 0x00100022, 0x00000003, 0x00004001, 0xffffffff, 0x05000036, 0x00100082, 0x00000001, + 0x0010001a, 0x00000004, 0x01000002, 0x01000015, 0x05000036, 0x00100022, 0x00000002, 0x0010003a, + 0x00000005, 0x05000036, 0x00100082, 0x00000002, 0x0010000a, 0x00000004, 0x0700001e, 0x00100012, + 0x00000003, 0x0010000a, 0x00000003, 0x00004001, 0x00000001, 0x05000036, 0x00100042, 0x00000002, + 0x0010003a, 0x00000005, 0x05000036, 0x00100032, 0x00000003, 0x00100086, 0x00000003, 0x01000016, + 0x09000037, 0x00100042, 0x00000001, 0x0010001a, 0x00000003, 0x0010003a, 0x00000001, 0x0010002a, + 0x00000002, 0x01000012, 0x05000036, 0x00100042, 0x00000001, 0x0010003a, 0x00000003, 0x01000015, + 0x08000038, 0x00100012, 0x00000001, 0x0010002a, 0x00000001, 0x0020801a, 0x00000000, 0x00000005, + 0x01000015, 0x0300001f, 0x0010001a, 0x00000001, 0x08000020, 0x00100022, 0x00000001, 0x0020800a, + 0x00000000, 0x00000005, 0x00004001, 0x00000003, 0x0304001f, 0x0010001a, 0x00000001, 0x0800000f, + 0x00100042, 0x00000001, 0x00101046, 0x00000000, 0x00208046, 0x00000000, 0x00000006, 0x08000000, + 0x00100012, 0x00000002, 0x0010002a, 0x00000001, 0x0020802a, 0x00000000, 0x00000006, 0x0800000f, + 0x00100042, 0x00000001, 0x00101046, 0x00000000, 0x00208046, 0x00000000, 0x00000007, 0x08000000, + 0x00100022, 0x00000002, 0x0010002a, 0x00000001, 0x0020802a, 0x00000000, 0x00000007, 0x09000045, + 0x001000f2, 0x00000002, 0x00100046, 0x00000002, 0x00107e46, 0x00000001, 0x00106000, 0x00000001, + 0x0a000037, 0x00100042, 0x00000001, 0x0020803a, 0x00000000, 0x00000007, 0x00004001, 0x3f800000, + 0x0010003a, 0x00000002, 0x08000038, 0x00100012, 0x00000001, 0x0010002a, 0x00000001, 0x0020801a, + 0x00000000, 0x00000005, 0x01000015, 0x0a000037, 0x00100012, 0x00000001, 0x0010001a, 0x00000001, + 0x0010000a, 0x00000001, 0x0020801a, 0x00000000, 0x00000005, 0x01000015, 0x01000015, 0x01000015, + 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00100006, 0x00000001, 0x01000012, + 0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x01000015, 0x0404001f, 0x0020800a, + 0x00000000, 0x00000000, 0x0500000b, 0x00100032, 0x00000000, 0x00101046, 0x00000001, 0x0500000c, + 0x001000c2, 0x00000000, 0x00101406, 0x00000001, 0x08000027, 0x00100012, 0x00000001, 0x0020801a, + 0x00000000, 0x00000000, 0x00004001, 0x00000000, 0x0500003b, 0x00100022, 0x00000001, 0x0010000a, + 0x00000001, 0x07000000, 0x001000c2, 0x00000001, 0x00101406, 0x00000001, 0x00101406, 0x00000001, + 0x07000038, 0x001000f2, 0x00000002, 0x00100d86, 0x00000000, 0x00100fa6, 0x00000001, 0x0a000032, + 0x00100032, 0x00000000, 0x00100aa6, 0x00000001, 0x00100086, 0x00000000, 0x801005d6, 0x00000041, + 0x00000000, 0x0700000f, 0x00100042, 0x00000000, 0x00100046, 0x00000000, 0x00101ae6, 0x00000001, + 0x07000031, 0x00100042, 0x00000000, 0x0010002a, 0x00000000, 0x00004001, 0x00000000, 0x07000001, + 0x00100042, 0x00000000, 0x0010001a, 0x00000001, 0x0010002a, 0x00000000, 0x0304000d, 0x0010002a, + 0x00000000, 0x07000038, 0x00100062, 0x00000000, 0x00100556, 0x00000000, 0x00101106, 0x00000003, + 0x09000032, 0x00100032, 0x00000000, 0x00101046, 0x00000002, 0x00100006, 0x00000000, 0x00100596, + 0x00000000, 0x0700000f, 0x00100012, 0x00000000, 0x00100046, 0x00000000, 0x00100046, 0x00000000, + 0x0500004b, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x07000038, 0x00100062, 0x00000000, + 0x00101106, 0x00000001, 0x00101106, 0x00000001, 0x0a000032, 0x00100082, 0x00000000, 0x0010100a, + 0x00000001, 0x0010100a, 0x00000001, 0x8010101a, 0x00000041, 0x00000001, 0x08000000, 0x00100012, + 0x00000000, 0x8010003a, 0x000000c1, 0x00000000, 0x0010000a, 0x00000000, 0x07000031, 0x00100012, + 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000000, 0x07000001, 0x00100012, 0x00000000, + 0x0010001a, 0x00000001, 0x0010000a, 0x00000000, 0x0304000d, 0x0010000a, 0x00000000, 0x07000000, + 0x00100092, 0x00000000, 0x00100ea6, 0x00000002, 0x00100406, 0x00000002, 0x0700000f, 0x00100022, + 0x00000001, 0x001000c6, 0x00000000, 0x00101ae6, 0x00000001, 0x07000031, 0x00100022, 0x00000001, + 0x0010001a, 0x00000001, 0x00004001, 0x00000000, 0x07000001, 0x00100022, 0x00000001, 0x0010000a, + 0x00000001, 0x0010001a, 0x00000001, 0x0304000d, 0x0010001a, 0x00000001, 0x07000038, 0x00100062, + 0x00000001, 0x00100ff6, 0x00000000, 0x00101106, 0x00000003, 0x09000032, 0x00100092, 0x00000000, + 0x00101406, 0x00000002, 0x00100006, 0x00000000, 0x00100956, 0x00000001, 0x0700000f, 0x00100012, + 0x00000000, 0x001000c6, 0x00000000, 0x001000c6, 0x00000000, 0x0500004b, 0x00100012, 0x00000000, + 0x0010000a, 0x00000000, 0x07000000, 0x00100022, 0x00000000, 0x0010002a, 0x00000000, 0x0010001a, + 0x00000000, 0x07000000, 0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0xbf800000, + 0x08000000, 0x00100012, 0x00000000, 0x8010001a, 0x000000c1, 0x00000000, 0x0010000a, 0x00000000, + 0x07000031, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000000, 0x07000001, + 0x00100012, 0x00000000, 0x0010000a, 0x00000001, 0x0010000a, 0x00000000, 0x0304000d, 0x0010000a, + 0x00000000, 0x01000012, 0x08000027, 0x00100012, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, + 0x00004001, 0x00000000, 0x0500003b, 0x00100022, 0x00000000, 0x0010000a, 0x00000000, 0x07000038, + 0x001000c2, 0x00000000, 0x00101406, 0x00000001, 0x00101406, 0x00000001, 0x0a000032, 0x00100012, + 0x00000001, 0x0010100a, 0x00000001, 0x0010100a, 0x00000001, 0x8010101a, 0x00000041, 0x00000001, + 0x07000038, 0x00100012, 0x00000001, 0x0010000a, 0x00000001, 0x0010102a, 0x00000001, 0x07000031, + 0x00100012, 0x00000001, 0x0010000a, 0x00000001, 0x00004001, 0x00000000, 0x07000001, 0x00100022, + 0x00000000, 0x0010001a, 0x00000000, 0x0010000a, 0x00000001, 0x0304000d, 0x0010001a, 0x00000000, + 0x07000000, 0x00100022, 0x00000000, 0x0010003a, 0x00000000, 0x0010002a, 0x00000000, 0x07000000, + 0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0xbf800000, 0x07000038, 0x00100022, + 0x00000000, 0x0010001a, 0x00000000, 0x0010102a, 0x00000001, 0x07000031, 0x00100022, 0x00000000, + 0x0010001a, 0x00000000, 0x00004001, 0x00000000, 0x07000001, 0x00100012, 0x00000000, 0x0010000a, + 0x00000000, 0x0010001a, 0x00000000, 0x0304000d, 0x0010000a, 0x00000000, 0x01000015, 0x0100003e, + }; + static const struct shape_info + { + enum d2d_shape_type shape_type; + const D3D11_INPUT_ELEMENT_DESC *il_desc; + unsigned int il_element_count; + const void *vs_code; + size_t vs_code_size; + } + shape_info[] = + { + {D2D_SHAPE_TYPE_OUTLINE, il_desc_outline, ARRAY_SIZE(il_desc_outline), + vs_code_outline, sizeof(vs_code_outline)}, + {D2D_SHAPE_TYPE_BEZIER_OUTLINE, il_desc_curve_outline, ARRAY_SIZE(il_desc_curve_outline), + vs_code_bezier_outline, sizeof(vs_code_bezier_outline)}, + {D2D_SHAPE_TYPE_ARC_OUTLINE, il_desc_curve_outline, ARRAY_SIZE(il_desc_curve_outline), + vs_code_arc_outline, sizeof(vs_code_arc_outline)}, + {D2D_SHAPE_TYPE_TRIANGLE, il_desc_triangle, ARRAY_SIZE(il_desc_triangle), + vs_code_triangle, sizeof(vs_code_triangle)}, + {D2D_SHAPE_TYPE_CURVE, il_desc_curve, ARRAY_SIZE(il_desc_curve), + vs_code_curve, sizeof(vs_code_curve)}, + }; + static const struct + { + float x, y; + } + quad[] = + { + {-1.0f, 1.0f}, + {-1.0f, -1.0f}, + { 1.0f, 1.0f}, + { 1.0f, -1.0f}, + }; + static const UINT16 indices[] = {0, 1, 2, 2, 1, 3}; + static const D3D_FEATURE_LEVEL feature_levels = D3D_FEATURE_LEVEL_10_0; + + render_target->ID2D1DeviceContext1_iface.lpVtbl = &d2d_device_context_vtbl; + render_target->ID2D1GdiInteropRenderTarget_iface.lpVtbl = &d2d_gdi_interop_render_target_vtbl; + render_target->IDWriteTextRenderer_iface.lpVtbl = &d2d_text_renderer_vtbl; + render_target->IUnknown_iface.lpVtbl = &d2d_device_context_inner_unknown_vtbl; + render_target->refcount = 1; + ID2D1Device1_GetFactory(&device->ID2D1Device1_iface, &render_target->factory); + render_target->device = device; + ID2D1Device1_AddRef(&render_target->device->ID2D1Device1_iface); + + render_target->outer_unknown = outer_unknown ? outer_unknown : &render_target->IUnknown_iface; + render_target->ops = ops; + + device_impl = unsafe_impl_from_ID2D1Device((ID2D1Device1 *)device); + if (FAILED(hr = IDXGIDevice_QueryInterface(device_impl->dxgi_device, + &IID_ID3D11Device1, (void **)&render_target->d3d_device))) + { + WARN("Failed to query ID3D11Device1 interface, hr %#lx.\n", hr); + goto err; + } + + if (FAILED(hr = ID3D11Device1_CreateDeviceContextState(render_target->d3d_device, + 0, &feature_levels, 1, D3D11_SDK_VERSION, &IID_ID3D11Device1, NULL, + &render_target->d3d_state))) + { + WARN("Failed to create device context state, hr %#lx.\n", hr); + goto err; + } + + for (i = 0; i < ARRAY_SIZE(shape_info); ++i) + { + const struct shape_info *si = &shape_info[i]; + + if (FAILED(hr = ID3D11Device1_CreateInputLayout(render_target->d3d_device, si->il_desc, si->il_element_count, + si->vs_code, si->vs_code_size, &render_target->shape_resources[si->shape_type].il))) + { + WARN("Failed to create input layout for shape type %#x, hr %#lx.\n", si->shape_type, hr); + goto err; + } + + if (FAILED(hr = ID3D11Device1_CreateVertexShader(render_target->d3d_device, si->vs_code, + si->vs_code_size, NULL, &render_target->shape_resources[si->shape_type].vs))) + { + WARN("Failed to create vertex shader for shape type %#x, hr %#lx.\n", si->shape_type, hr); + goto err; + } + + } + + buffer_desc.ByteWidth = sizeof(struct d2d_vs_cb); + buffer_desc.Usage = D3D11_USAGE_DYNAMIC; + buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + buffer_desc.MiscFlags = 0; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, &buffer_desc, NULL, + &render_target->vs_cb))) + { + WARN("Failed to create constant buffer, hr %#lx.\n", hr); + goto err; + } + + if (FAILED(hr = ID3D11Device1_CreatePixelShader(render_target->d3d_device, + ps_code, sizeof(ps_code), NULL, &render_target->ps))) + { + WARN("Failed to create pixel shader, hr %#lx.\n", hr); + goto err; + } + + buffer_desc.ByteWidth = sizeof(struct d2d_ps_cb); + buffer_desc.Usage = D3D11_USAGE_DYNAMIC; + buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + buffer_desc.MiscFlags = 0; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, &buffer_desc, NULL, + &render_target->ps_cb))) + { + WARN("Failed to create constant buffer, hr %#lx.\n", hr); + goto err; + } + + buffer_desc.ByteWidth = sizeof(indices); + buffer_desc.Usage = D3D11_USAGE_DEFAULT; + buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER; + buffer_desc.CPUAccessFlags = 0; + buffer_desc.MiscFlags = 0; + + buffer_data.pSysMem = indices; + buffer_data.SysMemPitch = 0; + buffer_data.SysMemSlicePitch = 0; + + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, + &buffer_desc, &buffer_data, &render_target->ib))) + { + WARN("Failed to create clear index buffer, hr %#lx.\n", hr); + goto err; + } + + buffer_desc.ByteWidth = sizeof(quad); + buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + buffer_data.pSysMem = quad; + + render_target->vb_stride = sizeof(*quad); + if (FAILED(hr = ID3D11Device1_CreateBuffer(render_target->d3d_device, + &buffer_desc, &buffer_data, &render_target->vb))) + { + WARN("Failed to create clear vertex buffer, hr %#lx.\n", hr); + goto err; + } + + rs_desc.FillMode = D3D11_FILL_SOLID; + rs_desc.CullMode = D3D11_CULL_NONE; + rs_desc.FrontCounterClockwise = FALSE; + rs_desc.DepthBias = 0; + rs_desc.DepthBiasClamp = 0.0f; + rs_desc.SlopeScaledDepthBias = 0.0f; + rs_desc.DepthClipEnable = TRUE; + rs_desc.ScissorEnable = TRUE; + rs_desc.MultisampleEnable = FALSE; + rs_desc.AntialiasedLineEnable = FALSE; + if (FAILED(hr = ID3D11Device1_CreateRasterizerState(render_target->d3d_device, &rs_desc, &render_target->rs))) + { + WARN("Failed to create clear rasteriser state, hr %#lx.\n", hr); + goto err; + } + + if (FAILED(hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, + &IID_IDWriteFactory, (IUnknown **)&dwrite_factory))) + { + ERR("Failed to create dwrite factory, hr %#lx.\n", hr); + goto err; + } + + hr = IDWriteFactory_CreateRenderingParams(dwrite_factory, &render_target->default_text_rendering_params); + IDWriteFactory_Release(dwrite_factory); + if (FAILED(hr)) + { + ERR("Failed to create default text rendering parameters, hr %#lx.\n", hr); + goto err; + } + + render_target->drawing_state.transform = identity; + + if (!d2d_clip_stack_init(&render_target->clip_stack)) + { + WARN("Failed to initialize clip stack.\n"); + hr = E_FAIL; + goto err; + } + + render_target->desc.dpiX = 96.0f; + render_target->desc.dpiY = 96.0f; + + return S_OK; + +err: + if (render_target->default_text_rendering_params) + IDWriteRenderingParams_Release(render_target->default_text_rendering_params); + if (render_target->rs) + ID3D11RasterizerState_Release(render_target->rs); + if (render_target->vb) + ID3D11Buffer_Release(render_target->vb); + if (render_target->ib) + ID3D11Buffer_Release(render_target->ib); + if (render_target->ps_cb) + ID3D11Buffer_Release(render_target->ps_cb); + if (render_target->ps) + ID3D11PixelShader_Release(render_target->ps); + if (render_target->vs_cb) + ID3D11Buffer_Release(render_target->vs_cb); + for (i = 0; i < D2D_SHAPE_TYPE_COUNT; ++i) + { + if (render_target->shape_resources[i].vs) + ID3D11VertexShader_Release(render_target->shape_resources[i].vs); + if (render_target->shape_resources[i].il) + ID3D11InputLayout_Release(render_target->shape_resources[i].il); + } + if (render_target->d3d_state) + ID3DDeviceContextState_Release(render_target->d3d_state); + if (render_target->d3d_device) + ID3D11Device1_Release(render_target->d3d_device); + ID2D1Device1_Release(&render_target->device->ID2D1Device1_iface); + ID2D1Factory_Release(render_target->factory); + return hr; +} + +HRESULT d2d_d3d_create_render_target(struct d2d_device *device, IDXGISurface *surface, IUnknown *outer_unknown, + const struct d2d_device_context_ops *ops, const D2D1_RENDER_TARGET_PROPERTIES *desc, void **render_target) +{ + D2D1_BITMAP_PROPERTIES1 bitmap_desc; + struct d2d_device_context *object; + ID2D1Bitmap1 *bitmap; + HRESULT hr; + + if (desc->type != D2D1_RENDER_TARGET_TYPE_DEFAULT && desc->type != D2D1_RENDER_TARGET_TYPE_HARDWARE) + WARN("Ignoring render target type %#x.\n", desc->type); + if (desc->usage != D2D1_RENDER_TARGET_USAGE_NONE) + FIXME("Ignoring render target usage %#x.\n", desc->usage); + if (desc->minLevel != D2D1_FEATURE_LEVEL_DEFAULT) + WARN("Ignoring feature level %#x.\n", desc->minLevel); + + bitmap_desc.dpiX = desc->dpiX; + bitmap_desc.dpiY = desc->dpiY; + + if (bitmap_desc.dpiX == 0.0f && bitmap_desc.dpiY == 0.0f) + { + bitmap_desc.dpiX = 96.0f; + bitmap_desc.dpiY = 96.0f; + } + else if (bitmap_desc.dpiX <= 0.0f || bitmap_desc.dpiY <= 0.0f) + return E_INVALIDARG; + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = d2d_device_context_init(object, device, outer_unknown, ops))) + { + WARN("Failed to initialise render target, hr %#lx.\n", hr); + free(object); + return hr; + } + + ID2D1DeviceContext1_SetDpi(&object->ID2D1DeviceContext1_iface, bitmap_desc.dpiX, bitmap_desc.dpiY); + + if (surface) + { + bitmap_desc.pixelFormat = desc->pixelFormat; + bitmap_desc.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW; + if (desc->usage & D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE) + bitmap_desc.bitmapOptions |= D2D1_BITMAP_OPTIONS_GDI_COMPATIBLE; + bitmap_desc.colorContext = NULL; + + if (FAILED(hr = ID2D1DeviceContext1_CreateBitmapFromDxgiSurface(&object->ID2D1DeviceContext1_iface, + surface, &bitmap_desc, &bitmap))) + { + WARN("Failed to create target bitmap, hr %#lx.\n", hr); + IUnknown_Release(&object->IUnknown_iface); + free(object); + return hr; + } + + ID2D1DeviceContext1_SetTarget(&object->ID2D1DeviceContext1_iface, (ID2D1Image *)bitmap); + ID2D1Bitmap1_Release(bitmap); + } + else + object->desc.pixelFormat = desc->pixelFormat; + + TRACE("Created render target %p.\n", object); + *render_target = outer_unknown ? &object->IUnknown_iface : (IUnknown *)&object->ID2D1DeviceContext1_iface; + + return S_OK; +} + +static HRESULT WINAPI d2d_device_QueryInterface(ID2D1Device1 *iface, REFIID iid, void **out) +{ + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_ID2D1Device1) + || IsEqualGUID(iid, &IID_ID2D1Device) + || IsEqualGUID(iid, &IID_ID2D1Resource) + || IsEqualGUID(iid, &IID_IUnknown)) + { + ID2D1Device1_AddRef(iface); + *out = iface; + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI d2d_device_AddRef(ID2D1Device1 *iface) +{ + struct d2d_device *device = impl_from_ID2D1Device(iface); + ULONG refcount = InterlockedIncrement(&device->refcount); + + TRACE("%p increasing refcount to %lu.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI d2d_device_Release(ID2D1Device1 *iface) +{ + struct d2d_device *device = impl_from_ID2D1Device(iface); + ULONG refcount = InterlockedDecrement(&device->refcount); + size_t i; + + TRACE("%p decreasing refcount to %lu.\n", iface, refcount); + + if (!refcount) + { + IDXGIDevice_Release(device->dxgi_device); + ID2D1Factory1_Release(device->factory); + for (i = 0; i < device->shaders.count; ++i) + IUnknown_Release(device->shaders.objects[i].shader); + free(device->shaders.objects); + free(device); + } + + return refcount; +} + +static void WINAPI d2d_device_GetFactory(ID2D1Device1 *iface, ID2D1Factory **factory) +{ + struct d2d_device *device = impl_from_ID2D1Device(iface); + + TRACE("iface %p, factory %p.\n", iface, factory); + + *factory = (ID2D1Factory *)device->factory; + ID2D1Factory1_AddRef(device->factory); +} + +static HRESULT d2d_device_create_device_context(struct d2d_device *device, + D2D1_DEVICE_CONTEXT_OPTIONS options, ID2D1DeviceContext1 **context) +{ + struct d2d_device_context *object; + HRESULT hr; + + if (options) + FIXME("Options are ignored %#x.\n", options); + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = d2d_device_context_init(object, device, NULL, NULL))) + { + WARN("Failed to initialise device context, hr %#lx.\n", hr); + free(object); + return hr; + } + + TRACE("Created device context %p.\n", object); + *context = &object->ID2D1DeviceContext1_iface; + + return S_OK; +} + +static HRESULT WINAPI d2d_device_CreateDeviceContext(ID2D1Device1 *iface, D2D1_DEVICE_CONTEXT_OPTIONS options, + ID2D1DeviceContext **context) +{ + struct d2d_device *device = impl_from_ID2D1Device(iface); + + TRACE("iface %p, options %#x, context %p.\n", iface, options, context); + + return d2d_device_create_device_context(device, options, (ID2D1DeviceContext1 **)context); +} + +static HRESULT WINAPI d2d_device_CreatePrintControl(ID2D1Device1 *iface, IWICImagingFactory *wic_factory, + IPrintDocumentPackageTarget *document_target, const D2D1_PRINT_CONTROL_PROPERTIES *desc, + ID2D1PrintControl **print_control) +{ + FIXME("iface %p, wic_factory %p, document_target %p, desc %p, print_control %p stub!\n", iface, wic_factory, + document_target, desc, print_control); + + return E_NOTIMPL; +} + +static void WINAPI d2d_device_SetMaximumTextureMemory(ID2D1Device1 *iface, UINT64 max_texture_memory) +{ + FIXME("iface %p, max_texture_memory %s stub!\n", iface, wine_dbgstr_longlong(max_texture_memory)); +} + +static UINT64 WINAPI d2d_device_GetMaximumTextureMemory(ID2D1Device1 *iface) +{ + FIXME("iface %p stub!\n", iface); + + return 0; +} + +static HRESULT WINAPI d2d_device_ClearResources(ID2D1Device1 *iface, UINT msec_since_use) +{ + FIXME("iface %p, msec_since_use %u stub!\n", iface, msec_since_use); + + return E_NOTIMPL; +} + +static D2D1_RENDERING_PRIORITY WINAPI d2d_device_GetRenderingPriority(ID2D1Device1 *iface) +{ + FIXME("iface %p stub!\n", iface); + + return D2D1_RENDERING_PRIORITY_NORMAL; +} + +static void WINAPI d2d_device_SetRenderingPriority(ID2D1Device1 *iface, D2D1_RENDERING_PRIORITY priority) +{ + FIXME("iface %p, priority %#x stub!\n", iface, priority); +} + +static HRESULT WINAPI d2d_device_CreateDeviceContext1(ID2D1Device1 *iface, D2D1_DEVICE_CONTEXT_OPTIONS options, + ID2D1DeviceContext1 **context) +{ + struct d2d_device *device = impl_from_ID2D1Device(iface); + + TRACE("iface %p, options %#x, context %p.\n", iface, options, context); + + return d2d_device_create_device_context(device, options, context); +} + +static const struct ID2D1Device1Vtbl d2d_device_vtbl = +{ + d2d_device_QueryInterface, + d2d_device_AddRef, + d2d_device_Release, + d2d_device_GetFactory, + d2d_device_CreateDeviceContext, + d2d_device_CreatePrintControl, + d2d_device_SetMaximumTextureMemory, + d2d_device_GetMaximumTextureMemory, + d2d_device_ClearResources, + d2d_device_GetRenderingPriority, + d2d_device_SetRenderingPriority, + d2d_device_CreateDeviceContext1, +}; + +struct d2d_device *unsafe_impl_from_ID2D1Device(ID2D1Device1 *iface) +{ + if (!iface) + return NULL; + assert(iface->lpVtbl == &d2d_device_vtbl); + return CONTAINING_RECORD(iface, struct d2d_device, ID2D1Device1_iface); +} + +void d2d_device_init(struct d2d_device *device, ID2D1Factory1 *iface, IDXGIDevice *dxgi_device) +{ + device->ID2D1Device1_iface.lpVtbl = &d2d_device_vtbl; + device->refcount = 1; + device->factory = iface; + ID2D1Factory1_AddRef(device->factory); + device->dxgi_device = dxgi_device; + IDXGIDevice_AddRef(device->dxgi_device); +} + +HRESULT d2d_device_add_shader(struct d2d_device *device, REFGUID shader_id, IUnknown *shader) +{ + struct d2d_shader *entry; + + if (!d2d_array_reserve((void **)&device->shaders.objects, &device->shaders.size, + device->shaders.count + 1, sizeof(*device->shaders.objects))) + { + WARN("Failed to resize shaders array.\n"); + return E_OUTOFMEMORY; + } + + entry = &device->shaders.objects[device->shaders.count++]; + entry->id = *shader_id; + entry->shader = shader; + IUnknown_AddRef(entry->shader); + + return S_OK; +} + +BOOL d2d_device_is_shader_loaded(struct d2d_device *device, REFGUID shader_id) +{ + size_t i; + + for (i = 0; i < device->shaders.count; ++i) + { + if (IsEqualGUID(shader_id, &device->shaders.objects[i].id)) + return TRUE; + } + + return FALSE; +} diff --git a/dlls/d2d1/stroke.c b/dlls/d2d1/stroke.c index 16fa1f60771..652b6d0b2a6 100644 --- a/dlls/d2d1/stroke.c +++ b/dlls/d2d1/stroke.c @@ -191,6 +191,14 @@ static const struct ID2D1StrokeStyle1Vtbl d2d_stroke_style_vtbl = d2d_stroke_style_GetStrokeTransformType };
+struct d2d_stroke_style *unsafe_impl_from_ID2D1StrokeStyle(ID2D1StrokeStyle *iface) +{ + if (!iface) + return NULL; + assert((const struct ID2D1StrokeStyle1Vtbl *)iface->lpVtbl == &d2d_stroke_style_vtbl); + return CONTAINING_RECORD(iface, struct d2d_stroke_style, ID2D1StrokeStyle1_iface); +} + HRESULT d2d_stroke_style_init(struct d2d_stroke_style *style, ID2D1Factory *factory, const D2D1_STROKE_STYLE_PROPERTIES1 *desc, const float *dashes, UINT32 dash_count) { @@ -211,7 +219,7 @@ HRESULT d2d_stroke_style_init(struct d2d_stroke_style *style, ID2D1Factory *fact if (desc->dashStyle > D2D1_DASH_STYLE_CUSTOM) return E_INVALIDARG;
- if (desc->transformType != D2D1_STROKE_TRANSFORM_TYPE_NORMAL) + if (desc->transformType != D2D1_STROKE_TRANSFORM_TYPE_NORMAL && desc->transformType != D2D1_STROKE_TRANSFORM_TYPE_FIXED) FIXME("transformType %d is not supported\n", desc->transformType);
style->ID2D1StrokeStyle1_iface.lpVtbl = &d2d_stroke_style_vtbl;
Nikolay Sivov (@nsivov) commented about dlls/d2d1/device.c:
return; }
- if (stroke_style)
FIXME("Ignoring stroke style %p.\n", stroke_style);
- if (stroke_style_impl)
- {
if (stroke_style_impl->desc.transformType == D2D1_STROKE_TRANSFORM_TYPE_FIXED)
stroke_width /= context->drawing_state.transform.m11;
- }
This looks incomplete, and definitely not enough to remove the fixme. Why is it only using m11 component?
- if (stroke_style)
FIXME("Ignoring stroke style %p.\n", stroke_style);
- if (stroke_style_impl)
- {
if (stroke_style_impl->desc.transformType == D2D1_STROKE_TRANSFORM_TYPE_FIXED)
stroke_width /= context->drawing_state.transform.m11;
- }
This looks incomplete, and definitely not enough to remove the fixme. Why is it only using m11 component?
The idea of using m11 was suggested by geometry.c,::StrokeContainsPoint(). FIXME() can be left as is if desired.