VisualNode.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Reactive.Disposables;
  7. using Avalonia.Media;
  8. using Avalonia.Platform;
  9. using Avalonia.Utilities;
  10. using Avalonia.VisualTree;
  11. namespace Avalonia.Rendering.SceneGraph
  12. {
  13. /// <summary>
  14. /// A node in the low-level scene graph representing an <see cref="IVisual"/>.
  15. /// </summary>
  16. internal class VisualNode : IVisualNode
  17. {
  18. private static readonly IReadOnlyList<IVisualNode> EmptyChildren = Array.Empty<IVisualNode>();
  19. private static readonly IReadOnlyList<IRef<IDrawOperation>> EmptyDrawOperations = Array.Empty<IRef<IDrawOperation>>();
  20. private Rect? _bounds;
  21. private double _opacity;
  22. private List<IVisualNode> _children;
  23. private List<IRef<IDrawOperation>> _drawOperations;
  24. private IRef<IDisposable> _drawOperationsRefCounter;
  25. private bool _drawOperationsCloned;
  26. private Matrix transformRestore;
  27. /// <summary>
  28. /// Initializes a new instance of the <see cref="VisualNode"/> class.
  29. /// </summary>
  30. /// <param name="visual">The visual that this node represents.</param>
  31. /// <param name="parent">The parent scene graph node, if any.</param>
  32. public VisualNode(IVisual visual, IVisualNode parent)
  33. {
  34. Contract.Requires<ArgumentNullException>(visual != null);
  35. Visual = visual;
  36. Parent = parent;
  37. HasAncestorGeometryClip = parent != null &&
  38. (parent.HasAncestorGeometryClip || parent.GeometryClip != null);
  39. }
  40. /// <inheritdoc/>
  41. public IVisual Visual { get; }
  42. /// <inheritdoc/>
  43. public IVisualNode Parent { get; }
  44. /// <inheritdoc/>
  45. public Matrix Transform { get; set; }
  46. /// <inheritdoc/>
  47. public Rect Bounds => _bounds ?? CalculateBounds();
  48. /// <inheritdoc/>
  49. public Rect ClipBounds { get; set; }
  50. /// <inheritdoc/>
  51. public Rect LayoutBounds { get; set; }
  52. /// <inheritdoc/>
  53. public bool ClipToBounds { get; set; }
  54. /// <inheritdoc/>
  55. public IGeometryImpl GeometryClip { get; set; }
  56. /// <inheritdoc/>
  57. public bool HasAncestorGeometryClip { get; }
  58. /// <inheritdoc/>
  59. public double Opacity
  60. {
  61. get { return _opacity; }
  62. set
  63. {
  64. if (_opacity != value)
  65. {
  66. _opacity = value;
  67. OpacityChanged = true;
  68. }
  69. }
  70. }
  71. /// <summary>
  72. /// Gets or sets the opacity mask for the scene graph node.
  73. /// </summary>
  74. public IBrush OpacityMask { get; set; }
  75. /// <summary>
  76. /// Gets a value indicating whether this node in the scene graph has already
  77. /// been updated in the current update pass.
  78. /// </summary>
  79. public bool SubTreeUpdated { get; set; }
  80. /// <summary>
  81. /// Gets a value indicating whether the <see cref="Opacity"/> property has changed.
  82. /// </summary>
  83. public bool OpacityChanged { get; private set; }
  84. public IVisual LayerRoot { get; set; }
  85. /// <inheritdoc/>
  86. public IReadOnlyList<IVisualNode> Children => _children ?? EmptyChildren;
  87. /// <inheritdoc/>
  88. public IReadOnlyList<IRef<IDrawOperation>> DrawOperations => _drawOperations ?? EmptyDrawOperations;
  89. /// <summary>
  90. /// Adds a child to the <see cref="Children"/> collection.
  91. /// </summary>
  92. /// <param name="child">The child to add.</param>
  93. public void AddChild(IVisualNode child)
  94. {
  95. if (child.Disposed)
  96. {
  97. throw new ObjectDisposedException("Visual node for {node.Visual}");
  98. }
  99. EnsureChildrenCreated();
  100. _children.Add(child);
  101. }
  102. /// <summary>
  103. /// Adds an operation to the <see cref="DrawOperations"/> collection.
  104. /// </summary>
  105. /// <param name="operation">The operation to add.</param>
  106. public void AddDrawOperation(IRef<IDrawOperation> operation)
  107. {
  108. EnsureDrawOperationsCreated();
  109. _drawOperations.Add(operation.Clone());
  110. }
  111. /// <summary>
  112. /// Removes a child from the <see cref="Children"/> collection.
  113. /// </summary>
  114. /// <param name="child">The child to remove.</param>
  115. public void RemoveChild(IVisualNode child)
  116. {
  117. EnsureChildrenCreated();
  118. _children.Remove(child);
  119. }
  120. /// <summary>
  121. /// Replaces a child in the <see cref="Children"/> collection.
  122. /// </summary>
  123. /// <param name="index">The child to be replaced.</param>
  124. /// <param name="node">The child to add.</param>
  125. public void ReplaceChild(int index, IVisualNode node)
  126. {
  127. if (node.Disposed)
  128. {
  129. throw new ObjectDisposedException("Visual node for {node.Visual}");
  130. }
  131. EnsureChildrenCreated();
  132. _children[index] = node;
  133. }
  134. /// <summary>
  135. /// Replaces an item in the <see cref="DrawOperations"/> collection.
  136. /// </summary>
  137. /// <param name="index">The operation to be replaced.</param>
  138. /// <param name="operation">The operation to add.</param>
  139. public void ReplaceDrawOperation(int index, IRef<IDrawOperation> operation)
  140. {
  141. EnsureDrawOperationsCreated();
  142. var old = _drawOperations[index];
  143. _drawOperations[index] = operation.Clone();
  144. old.Dispose();
  145. }
  146. /// <summary>
  147. /// Sorts the <see cref="Children"/> collection according to the order of the visual's
  148. /// children and their z-index.
  149. /// </summary>
  150. /// <param name="scene">The scene that the node is a part of.</param>
  151. public void SortChildren(Scene scene)
  152. {
  153. if (_children == null || _children.Count <= 1)
  154. {
  155. return;
  156. }
  157. var keys = new List<long>();
  158. for (var i = 0; i < Visual.VisualChildren.Count; ++i)
  159. {
  160. var child = Visual.VisualChildren[i];
  161. var zIndex = child.ZIndex;
  162. keys.Add(((long)zIndex << 32) + i);
  163. }
  164. keys.Sort();
  165. _children.Clear();
  166. foreach (var i in keys)
  167. {
  168. var child = Visual.VisualChildren[(int)(i & 0xffffffff)];
  169. var node = scene.FindNode(child);
  170. if (node != null)
  171. {
  172. _children.Add(node);
  173. }
  174. }
  175. }
  176. /// <summary>
  177. /// Removes items in the <see cref="Children"/> collection from the specified index
  178. /// to the end.
  179. /// </summary>
  180. /// <param name="first">The index of the first child to be removed.</param>
  181. public void TrimChildren(int first)
  182. {
  183. if (first < _children?.Count)
  184. {
  185. EnsureChildrenCreated();
  186. for (int i = first; i < _children.Count - first; i++)
  187. {
  188. _children[i].Dispose();
  189. }
  190. _children.RemoveRange(first, _children.Count - first);
  191. }
  192. }
  193. /// <summary>
  194. /// Removes items in the <see cref="DrawOperations"/> collection from the specified index
  195. /// to the end.
  196. /// </summary>
  197. /// <param name="first">The index of the first operation to be removed.</param>
  198. public void TrimDrawOperations(int first)
  199. {
  200. if (first < _drawOperations?.Count)
  201. {
  202. EnsureDrawOperationsCreated();
  203. for (int i = first; i < _drawOperations.Count; i++)
  204. {
  205. _drawOperations[i].Dispose();
  206. }
  207. _drawOperations.RemoveRange(first, _drawOperations.Count - first);
  208. }
  209. }
  210. /// <summary>
  211. /// Makes a copy of the node
  212. /// </summary>
  213. /// <param name="parent">The new parent node.</param>
  214. /// <returns>A cloned node.</returns>
  215. public VisualNode Clone(IVisualNode parent)
  216. {
  217. return new VisualNode(Visual, parent)
  218. {
  219. Transform = Transform,
  220. ClipBounds = ClipBounds,
  221. ClipToBounds = ClipToBounds,
  222. LayoutBounds = LayoutBounds,
  223. GeometryClip = GeometryClip,
  224. _opacity = Opacity,
  225. OpacityMask = OpacityMask,
  226. _drawOperations = _drawOperations,
  227. _drawOperationsRefCounter = _drawOperationsRefCounter?.Clone(),
  228. _drawOperationsCloned = true,
  229. LayerRoot= LayerRoot,
  230. };
  231. }
  232. /// <inheritdoc/>
  233. public bool HitTest(Point p)
  234. {
  235. foreach (var operation in DrawOperations)
  236. {
  237. if (operation?.Item?.HitTest(p) == true)
  238. {
  239. return true;
  240. }
  241. }
  242. return false;
  243. }
  244. /// <inheritdoc/>
  245. public void BeginRender(IDrawingContextImpl context, bool skipOpacity)
  246. {
  247. transformRestore = context.Transform;
  248. if (ClipToBounds)
  249. {
  250. context.Transform = Matrix.Identity;
  251. context.PushClip(ClipBounds);
  252. }
  253. context.Transform = Transform;
  254. if (Opacity != 1 && !skipOpacity)
  255. {
  256. context.PushOpacity(Opacity);
  257. }
  258. if (GeometryClip != null)
  259. {
  260. context.PushGeometryClip(GeometryClip);
  261. }
  262. if (OpacityMask != null)
  263. {
  264. context.PushOpacityMask(OpacityMask, LayoutBounds);
  265. }
  266. }
  267. /// <inheritdoc/>
  268. public void EndRender(IDrawingContextImpl context, bool skipOpacity)
  269. {
  270. if (OpacityMask != null)
  271. {
  272. context.PopOpacityMask();
  273. }
  274. if (GeometryClip != null)
  275. {
  276. context.PopGeometryClip();
  277. }
  278. if (Opacity != 1 && !skipOpacity)
  279. {
  280. context.PopOpacity();
  281. }
  282. if (ClipToBounds)
  283. {
  284. context.Transform = Matrix.Identity;
  285. context.PopClip();
  286. }
  287. context.Transform = transformRestore;
  288. }
  289. private Rect CalculateBounds()
  290. {
  291. var result = new Rect();
  292. foreach (var operation in DrawOperations)
  293. {
  294. result = result.Union(operation.Item.Bounds);
  295. }
  296. _bounds = result;
  297. return result;
  298. }
  299. private void EnsureChildrenCreated()
  300. {
  301. if (_children == null)
  302. {
  303. _children = new List<IVisualNode>();
  304. }
  305. }
  306. /// <summary>
  307. /// Ensures that this node draw operations have been created and are mutable (in case we are using cloned operations).
  308. /// </summary>
  309. private void EnsureDrawOperationsCreated()
  310. {
  311. if (_drawOperations == null)
  312. {
  313. _drawOperations = new List<IRef<IDrawOperation>>();
  314. _drawOperationsRefCounter = RefCountable.Create(CreateDisposeDrawOperations(_drawOperations));
  315. _drawOperationsCloned = false;
  316. }
  317. else if (_drawOperationsCloned)
  318. {
  319. _drawOperations = new List<IRef<IDrawOperation>>(_drawOperations.Select(op => op.Clone()));
  320. _drawOperationsRefCounter.Dispose();
  321. _drawOperationsRefCounter = RefCountable.Create(CreateDisposeDrawOperations(_drawOperations));
  322. _drawOperationsCloned = false;
  323. }
  324. }
  325. /// <summary>
  326. /// Creates disposable that will dispose all items in passed draw operations after being disposed.
  327. /// It is crucial that we don't capture current <see cref="VisualNode"/> instance
  328. /// as draw operations can be cloned and may persist across subsequent scenes.
  329. /// </summary>
  330. /// <param name="drawOperations">Draw operations that need to be disposed.</param>
  331. /// <returns>Disposable for given draw operations.</returns>
  332. private static IDisposable CreateDisposeDrawOperations(List<IRef<IDrawOperation>> drawOperations)
  333. {
  334. return Disposable.Create(() =>
  335. {
  336. foreach (var operation in drawOperations)
  337. {
  338. operation.Dispose();
  339. }
  340. });
  341. }
  342. public bool Disposed { get; private set; }
  343. public void Dispose()
  344. {
  345. _drawOperationsRefCounter?.Dispose();
  346. Disposed = true;
  347. }
  348. }
  349. }