// Copyright (c) The Perspex Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using Perspex.Media; using Perspex.Platform; namespace Perspex.Rendering { /// /// Base class for standard renderers. /// /// /// This class provides implements the platform-independent parts of . /// [SuppressMessage("ReSharper", "LoopCanBeConvertedToQuery")] [SuppressMessage("ReSharper", "ForCanBeConvertedToForeach")] public static class RendererMixin { static int s_frameNum; static int s_fps; static int s_currentFrames; static TimeSpan s_lastMeasure; static Stopwatch s_stopwatch = Stopwatch.StartNew(); /// /// Renders the specified visual. /// /// IRenderer instance /// The visual to render. public static void Render(this IRenderTarget renderTarget, IVisual visual) { using (var ctx = renderTarget.CreateDrawingContext()) { ctx.Render(visual); s_frameNum++; if (DrawFpsCounter) { s_currentFrames++; var now = s_stopwatch.Elapsed; var elapsed = now - s_lastMeasure; if (elapsed.TotalSeconds > 0) { s_fps = (int) (s_currentFrames/elapsed.TotalSeconds); s_currentFrames = 0; s_lastMeasure = now; } var pt = new Point(40, 40); using ( var txt = new FormattedText("Frame #" + s_frameNum + " FPS: " + s_fps, "Arial", 18, FontStyle.Normal, TextAlignment.Left, FontWeight.Normal)) { ctx.FillRectangle(Brushes.White, new Rect(pt, txt.Measure())); ctx.DrawText(Brushes.Black, pt, txt); } } } } public static bool DrawFpsCounter { get; set; } /// /// Renders the specified visual. /// /// The visual to render. /// /// The drawing context. public static void Render(this DrawingContext context, IVisual visual) { var opacity = visual.Opacity; if (visual.IsVisible && opacity > 0) { var m = Matrix.CreateTranslation(visual.Bounds.Position); var renderTransform = Matrix.Identity; if (visual.RenderTransform != null) { var origin = visual.TransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height)); var offset = Matrix.CreateTranslation(origin); renderTransform = (-offset)*visual.RenderTransform.Value*(offset); } m = renderTransform*m; using (context.PushPostTransform(m)) using (context.PushOpacity(opacity)) using (visual.ClipToBounds ? context.PushClip(new Rect(visual.Bounds.Size)) : default(DrawingContext.PushedState)) using (context.PushTransformContainer()) { visual.Render(context); var lst = GetSortedVisualList(visual.VisualChildren); foreach (var child in lst) { context.Render(child); } ReturnListToPool(lst); } } } static readonly Stack> ListPool = new Stack>(); static readonly ZIndexComparer VisualComparer = new ZIndexComparer(); class ZIndexComparer : IComparer { public int Compare(IVisual x, IVisual y) => x.ZIndex.CompareTo(y.ZIndex); } static void ReturnListToPool(List lst) { lst.Clear(); ListPool.Push(lst); } static List GetSortedVisualList(IReadOnlyList source) { var lst = ListPool.Count == 0 ? new List() : ListPool.Pop(); for (var c = 0; c < source.Count; c++) lst.Add(source[c]); lst.Sort(VisualComparer); return lst; } } }