diff --git a/SpineViewer/Exporter/ExportArgs.cs b/SpineViewer/Exporter/ExportArgs.cs
index 308e2f3..52315f0 100644
--- a/SpineViewer/Exporter/ExportArgs.cs
+++ b/SpineViewer/Exporter/ExportArgs.cs
@@ -67,10 +67,12 @@ namespace SpineViewer.Exporter
public bool RenderSelectedOnly { get; }
///
- /// 背景颜色 TODO: 提供颜色编辑
+ /// 背景颜色
///
+ [Editor(typeof(SFMLColorEditor), typeof(UITypeEditor))]
+ [TypeConverter(typeof(SFMLColorConverter))]
[Category("[0] 导出"), DisplayName("背景颜色"), Description("要使用的背景色, 格式为 #RRGGBBAA")]
- public SFML.Graphics.Color BackgroundColor { get; set; } = SFML.Graphics.Color.Transparent;
+ public SFML.Graphics.Color BackgroundColor { get; set; } = new(0, 255, 0, 0);
///
/// 检查参数是否合法并规范化参数值, 否则返回用户错误原因
diff --git a/SpineViewer/Exporter/TypeConverter.cs b/SpineViewer/Exporter/TypeConverter.cs
index aa47c67..7fe0bb5 100644
--- a/SpineViewer/Exporter/TypeConverter.cs
+++ b/SpineViewer/Exporter/TypeConverter.cs
@@ -23,4 +23,81 @@ namespace SpineViewer.Exporter
return new StandardValuesCollection(supportedFileSuffix);
}
}
+
+ public class SFMLColorConverter : ExpandableObjectConverter
+ {
+ private class SFMLColorPropertyDescriptor : SimplePropertyDescriptor
+ {
+ public SFMLColorPropertyDescriptor(Type componentType, string name, Type propertyType) : base(componentType, name, propertyType) { }
+
+ public override object? GetValue(object? component)
+ {
+ return component?.GetType().GetField(Name)?.GetValue(component) ?? default;
+ }
+
+ public override void SetValue(object? component, object? value)
+ {
+ component?.GetType().GetField(Name)?.SetValue(component, value);
+ }
+ }
+
+ public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
+ {
+ return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
+ }
+
+ public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
+ {
+ if (value is string s)
+ {
+ s = s.Trim();
+ if (s.StartsWith("#") && s.Length == 9)
+ {
+ try
+ {
+ // 解析 R, G, B, A 分量,注意16进制解析
+ byte r = byte.Parse(s.Substring(1, 2), NumberStyles.HexNumber);
+ byte g = byte.Parse(s.Substring(3, 2), NumberStyles.HexNumber);
+ byte b = byte.Parse(s.Substring(5, 2), NumberStyles.HexNumber);
+ byte a = byte.Parse(s.Substring(7, 2), NumberStyles.HexNumber);
+ return new SFML.Graphics.Color(r, g, b, a);
+ }
+ catch (Exception ex)
+ {
+ throw new FormatException("无法解析颜色,确保格式为 #RRGGBBAA", ex);
+ }
+ }
+ throw new FormatException("格式错误,正确格式为 #RRGGBBAA");
+ }
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
+ {
+ return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
+ }
+
+ public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
+ {
+ if (destinationType == typeof(string) && value is SFML.Graphics.Color color)
+ return $"#{color.R:X2}{color.G:X2}{color.B:X2}{color.A:X2}";
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+
+ public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes)
+ {
+ // 自定义属性集合
+ var properties = new List
+ {
+ // 定义 R, G, B, A 四个字段的描述器
+ new SFMLColorPropertyDescriptor(typeof(SFML.Graphics.Color), "R", typeof(byte)),
+ new SFMLColorPropertyDescriptor(typeof(SFML.Graphics.Color), "G", typeof(byte)),
+ new SFMLColorPropertyDescriptor(typeof(SFML.Graphics.Color), "B", typeof(byte)),
+ new SFMLColorPropertyDescriptor(typeof(SFML.Graphics.Color), "A", typeof(byte))
+ };
+
+ // 返回自定义属性集合
+ return new PropertyDescriptorCollection(properties.ToArray());
+ }
+ }
}
diff --git a/SpineViewer/Exporter/UITypeEditor.cs b/SpineViewer/Exporter/UITypeEditor.cs
new file mode 100644
index 0000000..e6e58b3
--- /dev/null
+++ b/SpineViewer/Exporter/UITypeEditor.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing.Design;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SpineViewer.Exporter
+{
+ class SFMLColorEditor : UITypeEditor
+ {
+ public override bool GetPaintValueSupported(ITypeDescriptorContext? context) => true;
+
+ public override void PaintValue(PaintValueEventArgs e)
+ {
+ if (e.Value is SFML.Graphics.Color color)
+ {
+ // 定义颜色和透明度的绘制区域
+ var colorBox = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width / 2, e.Bounds.Height);
+ var alphaBox = new Rectangle(e.Bounds.X + e.Bounds.Width / 2, e.Bounds.Y, e.Bounds.Width / 2, e.Bounds.Height);
+
+ // 转换为 System.Drawing.Color
+ var drawColor = Color.FromArgb(color.A, color.R, color.G, color.B);
+
+ // 绘制纯颜色(RGB 部分)
+ using (var brush = new SolidBrush(Color.FromArgb(color.R, color.G, color.B)))
+ {
+ e.Graphics.FillRectangle(brush, colorBox);
+ e.Graphics.DrawRectangle(Pens.Black, colorBox);
+ }
+
+ // 绘制带透明度效果的颜色
+ using (var checkerBrush = CreateTransparencyBrush())
+ {
+ e.Graphics.FillRectangle(checkerBrush, alphaBox); // 背景棋盘格
+ }
+ using (var brush = new SolidBrush(drawColor))
+ {
+ e.Graphics.FillRectangle(brush, alphaBox); // 叠加透明颜色
+ e.Graphics.DrawRectangle(Pens.Black, alphaBox);
+ }
+ }
+ else
+ {
+ base.PaintValue(e);
+ }
+ }
+
+ // 创建一个透明背景的棋盘格图案画刷
+ private static TextureBrush CreateTransparencyBrush()
+ {
+ var bitmap = new Bitmap(8, 8);
+ using (var g = Graphics.FromImage(bitmap))
+ {
+ g.Clear(Color.White);
+ using (var grayBrush = new SolidBrush(Color.LightGray))
+ {
+ g.FillRectangle(grayBrush, 0, 0, 4, 4);
+ g.FillRectangle(grayBrush, 4, 4, 4, 4);
+ }
+ }
+ return new TextureBrush(bitmap);
+ }
+ }
+}