StyleContext.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. using Drawie.Backend.Core.Numerics;
  2. using Drawie.Numerics;
  3. using PixiEditor.SVG.Enums;
  4. using PixiEditor.SVG.Features;
  5. using PixiEditor.SVG.Units;
  6. namespace PixiEditor.SVG;
  7. public struct StyleContext
  8. {
  9. public SvgProperty<SvgNumericUnit> StrokeWidth { get; }
  10. public SvgProperty<SvgColorUnit> Stroke { get; }
  11. public SvgProperty<SvgColorUnit> Fill { get; }
  12. public SvgProperty<SvgNumericUnit> FillOpacity { get; }
  13. public SvgProperty<SvgTransformUnit> Transform { get; }
  14. public SvgProperty<SvgEnumUnit<SvgStrokeLineCap>> StrokeLineCap { get; }
  15. public SvgProperty<SvgEnumUnit<SvgStrokeLineJoin>> StrokeLineJoin { get; }
  16. public SvgProperty<SvgNumericUnit> Opacity { get; }
  17. public SvgProperty<SvgStyleUnit> InlineStyle { get; set; }
  18. public VecD ViewboxOrigin { get; set; }
  19. public StyleContext()
  20. {
  21. StrokeWidth = new("stroke-width");
  22. Stroke = new("stroke");
  23. Fill = new("fill");
  24. FillOpacity = new("fill-opacity");
  25. Fill.Unit = new SvgColorUnit?(new SvgColorUnit("black"));
  26. Transform = new("transform");
  27. StrokeLineCap = new("stroke-linecap");
  28. StrokeLineJoin = new("stroke-linejoin");
  29. Opacity = new("opacity");
  30. InlineStyle = new("style");
  31. }
  32. public StyleContext(SvgDocument document)
  33. {
  34. StrokeWidth = FallbackToCssStyle(document.StrokeWidth, document.Style);
  35. Stroke = FallbackToCssStyle(document.Stroke, document.Style);
  36. Fill = FallbackToCssStyle(document.Fill, document.Style, new SvgColorUnit("black"));
  37. FillOpacity = FallbackToCssStyle(document.FillOpacity, document.Style);
  38. Transform = FallbackToCssStyle(document.Transform, document.Style, new SvgTransformUnit(Matrix3X3.Identity));
  39. StrokeLineCap = FallbackToCssStyle(document.StrokeLineCap, document.Style);
  40. StrokeLineJoin = FallbackToCssStyle(document.StrokeLineJoin, document.Style);
  41. Opacity = FallbackToCssStyle(document.Opacity, document.Style);
  42. ViewboxOrigin = new VecD(
  43. document.ViewBox.Unit.HasValue ? -document.ViewBox.Unit.Value.Value.X : 0,
  44. document.ViewBox.Unit.HasValue ? -document.ViewBox.Unit.Value.Value.Y : 0);
  45. InlineStyle = document.Style;
  46. }
  47. public StyleContext WithElement(SvgElement element)
  48. {
  49. StyleContext styleContext = Copy();
  50. styleContext.InlineStyle = MergeInlineStyle(element.Style, InlineStyle);
  51. if (element is ITransformable transformableElement)
  52. {
  53. if (styleContext.Transform.Unit == null)
  54. {
  55. styleContext.Transform.Unit =
  56. FallbackToCssStyle(transformableElement.Transform, styleContext.Transform, styleContext.InlineStyle)
  57. .Unit;
  58. }
  59. else
  60. {
  61. styleContext.Transform.Unit = new SvgTransformUnit(
  62. styleContext.Transform.Unit.Value.MatrixValue.Concat(
  63. FallbackToCssStyle(transformableElement.Transform, styleContext.InlineStyle).Unit
  64. ?.MatrixValue ??
  65. Matrix3X3.Identity));
  66. }
  67. }
  68. if (element is IFillable fillableElement)
  69. {
  70. styleContext.Fill.Unit = FallbackToCssStyle(fillableElement.Fill, styleContext.Fill,
  71. styleContext.InlineStyle, new SvgColorUnit("black")).Unit;
  72. styleContext.FillOpacity.Unit =
  73. FallbackToCssStyle(fillableElement.FillOpacity, styleContext.FillOpacity, styleContext.InlineStyle)
  74. .Unit;
  75. }
  76. if (element is IStrokable strokableElement)
  77. {
  78. styleContext.Stroke.Unit =
  79. FallbackToCssStyle(strokableElement.Stroke, styleContext.Stroke, styleContext.InlineStyle).Unit;
  80. styleContext.StrokeWidth.Unit =
  81. FallbackToCssStyle(strokableElement.StrokeWidth, styleContext.StrokeWidth, styleContext.InlineStyle)
  82. .Unit;
  83. styleContext.StrokeLineCap.Unit =
  84. FallbackToCssStyle(strokableElement.StrokeLineCap, styleContext.StrokeLineCap, styleContext.InlineStyle)
  85. .Unit;
  86. styleContext.StrokeLineJoin.Unit =
  87. FallbackToCssStyle(strokableElement.StrokeLineJoin, styleContext.StrokeLineJoin,
  88. styleContext.InlineStyle).Unit;
  89. }
  90. if (element is IOpacity opacityElement)
  91. {
  92. styleContext.Opacity.Unit =
  93. FallbackToCssStyle(opacityElement.Opacity, styleContext.Opacity, styleContext.InlineStyle).Unit;
  94. }
  95. return styleContext;
  96. }
  97. private StyleContext Copy()
  98. {
  99. StyleContext styleContext = new();
  100. if (StrokeWidth.Unit != null)
  101. {
  102. styleContext.StrokeWidth.Unit = StrokeWidth.Unit;
  103. }
  104. if (Stroke.Unit != null)
  105. {
  106. styleContext.Stroke.Unit = Stroke.Unit;
  107. }
  108. if (Fill.Unit != null)
  109. {
  110. styleContext.Fill.Unit = Fill.Unit;
  111. }
  112. if (FillOpacity.Unit != null)
  113. {
  114. styleContext.FillOpacity.Unit = FillOpacity.Unit;
  115. }
  116. if (Transform.Unit != null)
  117. {
  118. styleContext.Transform.Unit = Transform.Unit;
  119. }
  120. if (StrokeLineCap.Unit != null)
  121. {
  122. styleContext.StrokeLineCap.Unit = StrokeLineCap.Unit;
  123. }
  124. if (StrokeLineJoin.Unit != null)
  125. {
  126. styleContext.StrokeLineJoin.Unit = StrokeLineJoin.Unit;
  127. }
  128. if (Opacity.Unit != null)
  129. {
  130. styleContext.Opacity.Unit = Opacity.Unit;
  131. }
  132. styleContext.ViewboxOrigin = ViewboxOrigin;
  133. if (InlineStyle.Unit != null)
  134. {
  135. styleContext.InlineStyle.Unit = InlineStyle.Unit;
  136. }
  137. return styleContext;
  138. }
  139. private SvgProperty<TUnit>? FallbackToCssStyle<TUnit>(
  140. SvgProperty<TUnit> property,
  141. SvgProperty<SvgStyleUnit> inlineStyle, TUnit? fallback = null) where TUnit : struct, ISvgUnit
  142. {
  143. if (property.Unit != null)
  144. {
  145. return property;
  146. }
  147. SvgStyleUnit? style = inlineStyle.Unit;
  148. return style?.TryGetStyleFor<SvgProperty<TUnit>, TUnit>(property.SvgName)
  149. ?? (fallback.HasValue
  150. ? new SvgProperty<TUnit>(property.SvgName) { Unit = fallback.Value }
  151. : new SvgProperty<TUnit>(property.SvgName));
  152. }
  153. private SvgProperty<TUnit>? FallbackToCssStyle<TUnit>(
  154. SvgProperty<TUnit> property,
  155. SvgProperty<TUnit> parentStyleProperty,
  156. SvgProperty<SvgStyleUnit> inlineStyle, TUnit? fallback = null) where TUnit : struct, ISvgUnit
  157. {
  158. if (property.Unit != null)
  159. {
  160. return property;
  161. }
  162. SvgStyleUnit? style = inlineStyle.Unit;
  163. var styleProp = style?.TryGetStyleFor<SvgProperty<TUnit>, TUnit>(property.SvgName);
  164. if (styleProp != null) return styleProp;
  165. if(parentStyleProperty.Unit != null)
  166. {
  167. return parentStyleProperty;
  168. }
  169. return (fallback.HasValue
  170. ? new SvgProperty<TUnit>(property.SvgName) { Unit = fallback.Value }
  171. : new SvgProperty<TUnit>(property.SvgName));
  172. }
  173. private SvgProperty<SvgStyleUnit> MergeInlineStyle(SvgProperty<SvgStyleUnit> elementStyle,
  174. SvgProperty<SvgStyleUnit> parentStyle)
  175. {
  176. SvgStyleUnit? elementStyleUnit = elementStyle.Unit;
  177. SvgStyleUnit? parentStyleUnit = parentStyle.Unit;
  178. if (elementStyleUnit == null)
  179. {
  180. return parentStyle;
  181. }
  182. if (parentStyleUnit == null)
  183. {
  184. return elementStyle;
  185. }
  186. SvgStyleUnit style = parentStyleUnit.Value.MergeWith(elementStyleUnit.Value);
  187. return new SvgProperty<SvgStyleUnit>("style") { Unit = style };
  188. }
  189. }