// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Reflection; namespace SharpDX.Direct2D1 { /// /// Delegate used by to create a custom effect. /// /// A new instance of custom effect internal delegate CustomEffect CustomEffectFactoryDelegate(); /// /// Internal class used to keep reference to factory. /// internal class CustomEffectFactory { private Type customEffectType; private CreateCustomEffectDelegate callback; private XDocument xml; public CustomEffectFactory(CustomEffectFactoryDelegate factory, Type customEffectType) : this (factory, customEffectType, Utilities.GetGuidFromType(customEffectType)) { } public CustomEffectFactory(CustomEffectFactoryDelegate factory, Type customEffectType, Guid effectId) { this.customEffectType = customEffectType; // Gets the guid of this class Guid = effectId; unsafe { Factory = factory; callback = new CreateCustomEffectDelegate(CreateCustomEffectImpl); NativePointer = Marshal.GetFunctionPointerForDelegate(callback); } InitializeBindings(); InitializeXml(); } public Guid Guid { get; private set; } public CustomEffectFactoryDelegate Factory { get; private set; } public IntPtr NativePointer { get; private set; } public PropertyBinding[] Bindings { get; private set; } /// /// Converts custom effect to an xml description /// /// public string ToXml() { return string.Format("\r\n{0}", xml.ToString(SaveOptions.None)); } /// /// Initializes the property bindings /// private void InitializeBindings() { var bindings = new List(); #if BEFORE_NET45 foreach (var propertyInfo in customEffectType.GetTypeInfo().GetProperties(BindingFlags.Public|BindingFlags.Instance)) #else foreach (var propertyInfo in customEffectType.GetTypeInfo().DeclaredProperties) #endif { var binding = PropertyBinding.Get(customEffectType, propertyInfo); if (binding != null) bindings.Add(binding); } // Sort property bindings by property order // Don't rely on reflection to get property order, as there is no warranty bindings.Sort((left, right) => left.Attribute.Order.CompareTo(right.Attribute.Order)); Bindings = bindings.ToArray(); } private static XElement CreateXmlProperty(string name, string type, string value = null) { var property = new XElement("Property"); property.SetAttributeValue("name", name); property.SetAttributeValue("type", type); if (value != null) property.SetAttributeValue("value", value); return property; } /// /// Initializes the xml descriptor for this effect. /// private void InitializeXml() { xml = new XDocument(); var effect = new XElement("Effect"); xml.Add(effect); var customEffectTypeInfo = customEffectType.GetTypeInfo(); // Add var customEffectAttribute = Utilities.GetCustomAttribute(customEffectTypeInfo, true); effect.Add(CreateXmlProperty("DisplayName", "string", customEffectAttribute != null ? customEffectAttribute.DisplayName : customEffectTypeInfo.Name)); effect.Add(CreateXmlProperty("Author", "string", customEffectAttribute != null ? customEffectAttribute.Author : string.Empty)); effect.Add(CreateXmlProperty("Category", "string", customEffectAttribute != null ? customEffectAttribute.Category : string.Empty)); effect.Add(CreateXmlProperty("Description", "string", customEffectAttribute != null ? customEffectAttribute.Description : string.Empty)); var inputs = new XElement("Inputs"); var inputAttributes = Utilities.GetCustomAttributes(customEffectTypeInfo, true); foreach(var inputAttribute in inputAttributes) { var inputXml = new XElement("Input"); inputXml.SetAttributeValue("name", inputAttribute.Input); inputs.Add(inputXml); } effect.Add(inputs); // Add custom properties foreach(var binding in Bindings) { var property = CreateXmlProperty(binding.PropertyName, binding.TypeName); property.Add(CreateXmlProperty("DisplayName", "string", binding.PropertyName)); property.Add(CreateXmlProperty("Min", binding.TypeName, binding.Attribute.Min != null ? binding.Attribute.Min.ToString() : string.Empty)); property.Add(CreateXmlProperty("Max", binding.TypeName, binding.Attribute.Max != null ? binding.Attribute.Max.ToString() : string.Empty)); property.Add(CreateXmlProperty("Default", binding.TypeName, binding.Attribute.Default != null ? binding.Attribute.Default.ToString() : string.Empty)); effect.Add(property); } } /// HRESULT ID2D1EffectImpl::Initialize([In] ID2D1EffectContext* effectContext,[In] ID2D1TransformGraph* transformGraph) [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate int CreateCustomEffectDelegate(out IntPtr nativeCustomEffectPtr); private int CreateCustomEffectImpl(out IntPtr nativeCustomEffectPtr) { nativeCustomEffectPtr = IntPtr.Zero; try { var customEffect = Factory(); nativeCustomEffectPtr = CustomEffectShadow.ToIntPtr(customEffect); } catch (SharpDXException exception) { return exception.ResultCode.Code; } catch (Exception) { return Result.Fail.Code; } return Result.Ok.Code; } // Because we don't want any dependencies on Linq, used a minimalist compatible code to serialize XML private enum SaveOptions { None } private class XNode { private List nodes = new List(); public bool HasChildren { get { return nodes.Count > 0; } } public IEnumerable Children { get { return nodes; } } public void Add(XNode node) { nodes.Add(node); } public override string ToString() { return ToString(SaveOptions.None); } public string ToString(SaveOptions options) { var builder = new StringBuilder(); ToString(builder); return builder.ToString(); } protected virtual void ToString(StringBuilder builder) { foreach (var node in Children) { node.ToString(builder); } } } private class XDocument : XNode { } private class XElement : XNode { private List attributes = new List(); public XElement() { } public XElement(string name) { Name = name; } public string Name { get; set; } public void SetAttributeValue(string name, object value) { attributes.Add(new XElementAttribute(name, value)); } protected override void ToString(StringBuilder builder) { builder.AppendFormat("<{0}", Name); if (attributes.Count > 0) { foreach (var xElementAttribute in attributes) { builder.Append(" "); xElementAttribute.ToString(builder); } } if (HasChildren) { builder.AppendLine(">"); base.ToString(builder); builder.AppendFormat("", Name); builder.AppendLine(); } else { builder.AppendLine("/>"); } } } private class XElementAttribute { public XElementAttribute() { } public XElementAttribute(string name, object value) { Name = name; Value = value; } public string Name { get; set; } public object Value { get; set; } public void ToString(StringBuilder builder) { builder.AppendFormat("{0}='{1}'", Name, Value); } } } }