From: Ziqing Hui Subject: [PATCH v3 3/5] d2d1: Implement property parsing for RegisterEffectFromStream(). Message-Id: <20220614040856.1930461-3-zhui@codeweavers.com> Date: Tue, 14 Jun 2022 12:08:54 +0800 In-Reply-To: <20220614040856.1930461-1-zhui@codeweavers.com> References: <20220614040856.1930461-1-zhui@codeweavers.com> Signed-off-by: Ziqing Hui --- v3: Don't check EndElement name. Check required system property after XML parsing is done. dlls/d2d1/d2d1_private.h | 25 +++++ dlls/d2d1/factory.c | 211 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 234 insertions(+), 2 deletions(-) diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h index efc9247a822..b57c971a2ce 100644 --- a/dlls/d2d1/d2d1_private.h +++ b/dlls/d2d1/d2d1_private.h @@ -603,6 +603,15 @@ struct d2d_effect_context void d2d_effect_context_init(struct d2d_effect_context *effect_context, struct d2d_device_context *device_context) DECLSPEC_HIDDEN; +struct d2d_effect_property +{ + WCHAR *name; + D2D1_PROPERTY_TYPE type; + BYTE *value; + PD2D1_PROPERTY_SET_FUNCTION set_function; + PD2D1_PROPERTY_GET_FUNCTION get_function; +}; + struct d2d_effect_info { const CLSID *clsid; @@ -768,4 +777,20 @@ static inline const char *debug_d2d_ellipse(const D2D1_ELLIPSE *ellipse) ellipse->point.x, ellipse->point.y, ellipse->radiusX, ellipse->radiusY); } +static inline WCHAR *heap_strdupW(const WCHAR *str) +{ + WCHAR *ret = NULL; + size_t size; + + if(!str) + return ret; + + size = (wcslen(str) + 1) * sizeof(*str); + if(!(ret = heap_alloc(size))) + return ret; + memcpy(ret, str, size); + + return ret; +} + #endif /* __WINE_D2D1_PRIVATE_H */ diff --git a/dlls/d2d1/factory.c b/dlls/d2d1/factory.c index 273b699ef8d..3f48557ca37 100644 --- a/dlls/d2d1/factory.c +++ b/dlls/d2d1/factory.c @@ -37,6 +37,10 @@ struct d2d_effect_reg UINT32 input_count; + struct d2d_effect_property *properties; + size_t property_size; + size_t property_count; + struct list entry; }; @@ -68,9 +72,18 @@ static inline struct d2d_factory *impl_from_ID2D1Multithread(ID2D1Multithread *i static void d2d_effect_reg_cleanup(struct d2d_effect_reg *reg) { + size_t i; + if (!reg) return; + for (i = 0; i < reg->property_count; ++i) + { + heap_free(reg->properties[i].name); + heap_free(reg->properties[i].value); + } + heap_free(reg->properties); + heap_free(reg); } @@ -603,10 +616,155 @@ static BOOL next_xml_node(IXmlReader *xml_reader, XmlNodeType *node_type, const return S_FALSE; } +static HRESULT heap_get_attr(IXmlReader *xml_reader, const WCHAR *name, WCHAR **ptr) +{ + const WCHAR *attr_value; + + *ptr = NULL; + + if (IXmlReader_MoveToAttributeByName(xml_reader, name, NULL) != S_OK) + return E_INVALIDARG; + if (IXmlReader_GetValue(xml_reader, &attr_value, NULL) != S_OK) + return E_INVALIDARG; + if (!(*ptr = heap_strdupW(attr_value))) + return E_OUTOFMEMORY; + + return S_OK; +} + +static HRESULT get_property_type(IXmlReader *xml_reader, D2D1_PROPERTY_TYPE *type) +{ + const WCHAR *str; + unsigned int i; + + static const WCHAR *type_str[] = + { + L"", /* D2D1_PROPERTY_TYPE_UNKNOWN */ + L"string", /* D2D1_PROPERTY_TYPE_STRING */ + L"bool", /* D2D1_PROPERTY_TYPE_BOOL */ + L"uint32", /* D2D1_PROPERTY_TYPE_UINT32 */ + L"int32", /* D2D1_PROPERTY_TYPE_INT32 */ + L"float", /* D2D1_PROPERTY_TYPE_FLOAT */ + L"vector2", /* D2D1_PROPERTY_TYPE_VECTOR2 */ + L"vector3", /* D2D1_PROPERTY_TYPE_VECTOR3 */ + L"vector4", /* D2D1_PROPERTY_TYPE_VECTOR4 */ + L"blob", /* D2D1_PROPERTY_TYPE_BLOB */ + L"iunknown", /* D2D1_PROPERTY_TYPE_IUNKNOWN */ + L"enum", /* D2D1_PROPERTY_TYPE_ENUM */ + L"array", /* D2D1_PROPERTY_TYPE_ARRAY */ + L"clsid", /* D2D1_PROPERTY_TYPE_CLSID */ + L"matrix3x2", /* D2D1_PROPERTY_TYPE_MATRIX_3X2 */ + L"matrix4x3", /* D2D1_PROPERTY_TYPE_MATRIX_4X3 */ + L"matrix4x4", /* D2D1_PROPERTY_TYPE_MATRIX_4X4 */ + L"matrix5x4", /* D2D1_PROPERTY_TYPE_MATRIX_5X4 */ + L"colorcontext", /* D2D1_PROPERTY_TYPE_COLOR_CONTEXT */ + }; + + if (IXmlReader_MoveToAttributeByName(xml_reader, L"type", NULL) != S_OK) + return E_INVALIDARG; + if (IXmlReader_GetValue(xml_reader, &str, NULL) != S_OK) + return E_INVALIDARG; + + for (i = 0; i < ARRAY_SIZE(type_str); ++i) + { + if (!wcscmp(str, type_str[i])) + { + *type = i; + return S_OK; + } + } + + return E_INVALIDARG; +} + +static HRESULT add_property(struct d2d_effect_reg *reg, WCHAR *name, D2D1_PROPERTY_TYPE type, BYTE *value) +{ + struct d2d_effect_property *entry; + + if (!d2d_array_reserve((void **)®->properties, ®->property_size, reg->property_count + 1, sizeof(*reg->properties))) + { + ERR("Failed to resize properties array.\n"); + return E_OUTOFMEMORY; + } + + entry = ®->properties[reg->property_count++]; + entry->name = name; + entry->type = type; + entry->value = value; + entry->set_function = NULL; + entry->get_function = NULL; + + return S_OK; +} + +static HRESULT parse_sub_property(IXmlReader *xml_reader, struct d2d_effect_reg *reg) +{ + /* FIXME: Sub property is ignored. */ + return S_OK; +} + static HRESULT parse_property(IXmlReader *xml_reader, struct d2d_effect_reg *reg) { - /* FIXME: Property parsing is not implemented. */ - return S_OK; + WCHAR *name = NULL, *value = NULL; + D2D1_PROPERTY_TYPE type; + const WCHAR *node_name; + XmlNodeType node_type; + unsigned int i; + HRESULT hr; + + /* Get property name, type, value */ + if (FAILED(hr = heap_get_attr(xml_reader, L"name", &name))) + return hr; + if (FAILED(hr = get_property_type(xml_reader, &type))) + goto done; + heap_get_attr(xml_reader, L"value", &value); + + /* Check if property is already set */ + for (i = 0; i < reg->property_count; ++i) + { + if (!wcscmp(name, reg->properties[i].name)) + { + hr = E_INVALIDARG; + goto done; + } + } + + /* Parse sub properties */ + if (FAILED(hr = IXmlReader_MoveToElement(xml_reader))) + goto done; + if (!IXmlReader_IsEmptyElement(xml_reader)) + { + while ((hr = next_xml_node(xml_reader, &node_type, &node_name)) == S_OK) + { + if (node_type == XmlNodeType_Element && !wcscmp(node_name, L"Property")) + { + if (FAILED(hr = parse_sub_property(xml_reader, reg))) + goto done; + } + else if (node_type == XmlNodeType_EndElement) + { + break; + } + } + + if (FAILED(hr) || node_type != XmlNodeType_EndElement) + { + hr = E_INVALIDARG; + goto done; + } + } + + /* Add property to array */ + hr = add_property(reg, name, type, (BYTE *)value); + +done: + if (hr != S_OK) + { + heap_free(value); + heap_free(name); + } + + return hr; } static HRESULT parse_inputs(IXmlReader *xml_reader, struct d2d_effect_reg *reg) @@ -694,8 +852,10 @@ static HRESULT STDMETHODCALLTYPE d2d_factory_RegisterEffectFromStream(ID2D1Facto REFCLSID effect_id, IStream *property_xml, const D2D1_PROPERTY_BINDING *bindings, UINT32 binding_count, PD2D1_EFFECT_FACTORY effect_factory) { + BOOL display_name = FALSE, author = FALSE, category = FALSE, description = FALSE, wrong_type = FALSE, system_property; struct d2d_factory *factory = impl_from_ID2D1Factory3(iface); struct d2d_effect_reg *iter, *entry = NULL; + unsigned int i, j; HRESULT hr; TRACE("iface %p, effect_id %s, property_xml %p, bindings %p, binding_count %u, effect_factory %p.\n", @@ -719,6 +879,53 @@ static HRESULT STDMETHODCALLTYPE d2d_factory_RegisterEffectFromStream(ID2D1Facto return hr; } + /* Check system properties */ + for (i = 0; i < entry->property_count; ++i) + { + system_property = TRUE; + + if (!wcscmp(entry->properties[i].name, L"DisplayName")) + display_name = TRUE; + else if (!wcscmp(entry->properties[i].name, L"Author")) + author = TRUE; + else if (!wcscmp(entry->properties[i].name, L"Category")) + category = TRUE; + else if (!wcscmp(entry->properties[i].name, L"Description")) + description = TRUE; + else + system_property = FALSE; + + if (system_property && entry->properties[i].type != D2D1_PROPERTY_TYPE_STRING) + { + wrong_type = TRUE; + break; + } + } + if (!display_name || !author || !category || !description || wrong_type) + { + d2d_effect_reg_cleanup(entry); + return E_INVALIDARG; + } + + /* bind getter and setter to properties */ + for (i = 0; i < binding_count; ++i) + { + for (j = 0; j < entry->property_count; ++j) + { + if (!wcscmp(bindings[i].propertyName, entry->properties[j].name)) + { + entry->properties[j].get_function = bindings[i].getFunction; + entry->properties[j].set_function = bindings[i].setFunction; + break; + } + } + if (j == entry->property_count) + { + d2d_effect_reg_cleanup(entry); + return D2DERR_INVALID_PROPERTY; + } + } + entry->count = 1; entry->id = *effect_id; entry->factory = effect_factory; -- 2.25.1