TileBrushImplHelper.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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 Avalonia.Controls;
  4. using Avalonia.Media;
  5. using Avalonia.Rendering;
  6. using Avalonia.VisualTree;
  7. namespace Avalonia.RenderHelpers
  8. {
  9. internal class TileBrushImplHelper
  10. {
  11. public Size IntermediateSize { get; }
  12. public Rect DestinationRect { get; }
  13. private readonly TileMode _tileMode;
  14. private readonly Rect _sourceRect;
  15. private readonly Vector _scale;
  16. private readonly Vector _translate;
  17. private readonly Size _imageSize;
  18. private readonly IVisualBrush _visualBrush;
  19. private readonly IImageBrush _imageBrush;
  20. private readonly Matrix _transform;
  21. private readonly Rect _drawRect;
  22. public bool IsValid { get; }
  23. public TileBrushImplHelper(ITileBrush brush, Size targetSize)
  24. {
  25. _imageBrush = brush as IImageBrush;
  26. _visualBrush = brush as IVisualBrush;
  27. if (_imageBrush != null)
  28. {
  29. if (_imageBrush.Source == null)
  30. return;
  31. _imageSize = new Size(_imageBrush.Source.PixelWidth, _imageBrush.Source.PixelHeight);
  32. IsValid = true;
  33. }
  34. else if (_visualBrush != null)
  35. {
  36. var control = _visualBrush.Visual as IControl;
  37. if (control != null)
  38. {
  39. EnsureInitialized(control);
  40. if (control.IsArrangeValid == false)
  41. {
  42. control.Measure(Size.Infinity);
  43. control.Arrange(new Rect(control.DesiredSize));
  44. }
  45. _imageSize = control.Bounds.Size;
  46. IsValid = true;
  47. }
  48. }
  49. else
  50. return;
  51. _tileMode = brush.TileMode;
  52. _sourceRect = brush.SourceRect.ToPixels(_imageSize);
  53. DestinationRect = brush.DestinationRect.ToPixels(targetSize);
  54. _scale = brush.Stretch.CalculateScaling(DestinationRect.Size, _sourceRect.Size);
  55. _translate = CalculateTranslate(brush, _sourceRect, DestinationRect, _scale);
  56. IntermediateSize = CalculateIntermediateSize(_tileMode, targetSize, DestinationRect.Size);
  57. _transform = CalculateIntermediateTransform(
  58. _tileMode,
  59. _sourceRect,
  60. DestinationRect,
  61. _scale,
  62. _translate,
  63. out _drawRect);
  64. }
  65. public bool NeedsIntermediateSurface
  66. {
  67. get
  68. {
  69. if (_imageBrush == null)
  70. return true;
  71. if (_transform != Matrix.Identity)
  72. return true;
  73. if (_sourceRect.Position != default(Point))
  74. return true;
  75. if ((int) _sourceRect.Width != _imageBrush.Source.PixelWidth ||
  76. (int) _sourceRect.Height != _imageBrush.Source.PixelHeight)
  77. return true;
  78. return false;
  79. }
  80. }
  81. public T GetDirect<T>() => (T) _imageBrush?.Source.PlatformImpl;
  82. public void DrawIntermediate(DrawingContext ctx)
  83. {
  84. using (ctx.PushClip(_drawRect))
  85. using (ctx.PushPostTransform(_transform))
  86. {
  87. if (_imageBrush != null)
  88. {
  89. var bmpRc = new Rect(0, 0, _imageBrush.Source.PixelWidth, _imageBrush.Source.PixelHeight);
  90. ctx.DrawImage(_imageBrush.Source, 1, bmpRc, bmpRc);
  91. }
  92. else if (_visualBrush != null)
  93. {
  94. using (ctx.PushPostTransform(Matrix.CreateTranslation(-_visualBrush.Visual.Bounds.Position)))
  95. {
  96. ctx.Render(_visualBrush.Visual);
  97. }
  98. }
  99. }
  100. }
  101. /// <summary>
  102. /// Calculates a translate based on an <see cref="ITileBrush"/>, a source and destination
  103. /// rectangle and a scale.
  104. /// </summary>
  105. /// <param name="brush">The brush.</param>
  106. /// <param name="sourceRect">The source rectangle.</param>
  107. /// <param name="destinationRect">The destination rectangle.</param>
  108. /// <param name="scale">The _scale factor.</param>
  109. /// <returns>A vector with the X and Y _translate.</returns>
  110. public static Vector CalculateTranslate(
  111. ITileBrush brush,
  112. Rect sourceRect,
  113. Rect destinationRect,
  114. Vector scale)
  115. {
  116. var x = 0.0;
  117. var y = 0.0;
  118. var size = sourceRect.Size*scale;
  119. switch (brush.AlignmentX)
  120. {
  121. case AlignmentX.Center:
  122. x += (destinationRect.Width - size.Width)/2;
  123. break;
  124. case AlignmentX.Right:
  125. x += destinationRect.Width - size.Width;
  126. break;
  127. }
  128. switch (brush.AlignmentY)
  129. {
  130. case AlignmentY.Center:
  131. y += (destinationRect.Height - size.Height)/2;
  132. break;
  133. case AlignmentY.Bottom:
  134. y += destinationRect.Height - size.Height;
  135. break;
  136. }
  137. return new Vector(x, y);
  138. }
  139. public static Matrix CalculateIntermediateTransform(
  140. TileMode tileMode,
  141. Rect sourceRect,
  142. Rect destinationRect,
  143. Vector scale,
  144. Vector translate,
  145. out Rect drawRect)
  146. {
  147. var transform = Matrix.CreateTranslation(-sourceRect.Position)*
  148. Matrix.CreateScale(scale)*
  149. Matrix.CreateTranslation(translate);
  150. Rect dr;
  151. if (tileMode == TileMode.None)
  152. {
  153. dr = destinationRect;
  154. transform *= Matrix.CreateTranslation(destinationRect.Position);
  155. }
  156. else
  157. {
  158. dr = new Rect(destinationRect.Size);
  159. }
  160. drawRect = dr;
  161. return transform;
  162. }
  163. private static Size CalculateIntermediateSize(
  164. TileMode tileMode,
  165. Size targetSize,
  166. Size destinationSize) => tileMode == TileMode.None ? targetSize : destinationSize;
  167. private static void EnsureInitialized(IControl control)
  168. {
  169. foreach (var i in control.GetSelfAndVisualDescendents())
  170. {
  171. var c = i as IControl;
  172. if (c?.IsInitialized == false)
  173. {
  174. var init = c as ISupportInitialize;
  175. if (init != null)
  176. {
  177. init.BeginInit();
  178. init.EndInit();
  179. }
  180. }
  181. }
  182. }
  183. }
  184. }