C# 如何以每秒60帧的速度在WPF中快速绘制文本或图示符?
我想在WPF中创建一个用户控件,它可以使用单空格字体绘制一个n x m字符矩阵。 控件将尽快接受字符串[](目标为60 fps),并在屏幕上绘制它 我需要类似的性能 所有字符都使用相同的单空格字体绘制,但根据某些规则可能具有不同的颜色和背景(类似于VS中的语法高亮显示) 我在C#WinForms中实现了这个解决方案,没有任何问题,速度达到了60 FPS,但是当我想在WPF中学习如何做到这一点时,我只找到了几篇文章和帖子,描述了WPF性能的问题和相互冲突的信息 那么,在这种情况下,实现最高性能的最佳方法是什么 我尝试过的一种天真的方法是:C# 如何以每秒60帧的速度在WPF中快速绘制文本或图示符?,c#,wpf,C#,Wpf,我想在WPF中创建一个用户控件,它可以使用单空格字体绘制一个n x m字符矩阵。 控件将尽快接受字符串[](目标为60 fps),并在屏幕上绘制它 我需要类似的性能 所有字符都使用相同的单空格字体绘制,但根据某些规则可能具有不同的颜色和背景(类似于VS中的语法高亮显示) 我在C#WinForms中实现了这个解决方案,没有任何问题,速度达到了60 FPS,但是当我想在WPF中学习如何做到这一点时,我只找到了几篇文章和帖子,描述了WPF性能的问题和相互冲突的信息 那么,在这种情况下,实现最高性能的最
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Random rand = new Random();
public MainWindow()
{
InitializeComponent();
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(1);
timer.Tick += timer_Tick;
timer.Start();
}
string GenerateRandomString(int length)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++)
{
sb.Append(rand.Next(10));
}
return sb.ToString();
}
void timer_Tick(object sender, EventArgs e)
{
myTextBlock.Inlines.Clear();
for (int i = 0; i < 30; i++)
{
var run = new Run();
run.Text = GenerateRandomString(800);
run.Foreground = new SolidColorBrush(Color.FromArgb((byte)rand.Next(256),(byte)rand.Next(256),(byte)rand.Next(256),(byte)rand.Next(256)));
run.Background = new SolidColorBrush(Color.FromArgb((byte)rand.Next(256),(byte)rand.Next(256),(byte)rand.Next(256),(byte)rand.Next(256)));
myTextBlock.Inlines.Add(run);
}
}
}
//
///MainWindow.xaml的交互逻辑
///
公共部分类主窗口:窗口
{
Random rand=新的Random();
公共主窗口()
{
初始化组件();
调度程序计时器=新调度程序();
timer.Interval=TimeSpan.fromMillimes(1);
timer.Tick+=定时器_Tick;
timer.Start();
}
字符串生成器域字符串(整数长度)
{
StringBuilder sb=新的StringBuilder();
for(int i=0;i
问题是:你能在WPF中做得更好吗
附言。
是的,我可以直接使用DirectX,但这个问题是关于WPF而不是DX的。在WPF中绘制文本的最快方法可能是使用 下面是一个示例代码: main window.xaml
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
<Image>
<Image.Source>
<DrawingImage x:Name="drawingImage"/>
</Image.Source>
</Image>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
namespace WpfApplication
{
public partial class MainWindow : Window
{
Random rand = new Random();
Stopwatch stopwatch;
long frameCounter = 0;
GlyphTypeface glyphTypeface;
double renderingEmSize, advanceWidth, advanceHeight;
Point baselineOrigin;
public MainWindow()
{
InitializeComponent();
new Typeface("Consolas").TryGetGlyphTypeface(out this.glyphTypeface);
this.renderingEmSize = 10;
this.advanceWidth = this.glyphTypeface.AdvanceWidths[0] * this.renderingEmSize;
this.advanceHeight = this.glyphTypeface.Height * this.renderingEmSize;
this.baselineOrigin = new Point(0, this.glyphTypeface.Baseline * this.renderingEmSize);
CompositionTarget.Rendering += CompositionTarget_Rendering;
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(1000);
timer.Tick += timer_Tick;
timer.Start();
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
if (this.stopwatch == null)
this.stopwatch = Stopwatch.StartNew();
++this.frameCounter;
this.drawingImage.Drawing = this.Render();
}
string GenerateRandomString(int length)
{
var chars = new char[length];
for (int i = 0; i < chars.Length; ++i)
chars[i] = (char)rand.Next('A', 'Z' + 1);
return new string(chars);
}
void timer_Tick(object sender, EventArgs e)
{
var seconds = this.stopwatch.Elapsed.TotalSeconds;
Trace.WriteLine((long)(this.frameCounter / seconds));
if (seconds > 10)
{
this.stopwatch.Restart();
this.frameCounter = 0;
}
}
private Drawing Render()
{
var lines = new string[30];
for (int i = 0; i < lines.Length; ++i)
lines[i] = GenerateRandomString(100);
var drawing = new DrawingGroup();
using (var drawingContext = drawing.Open())
{
// TODO: draw rectangles which represent background.
// TODO: group of glyphs which has the same color should be drawn together.
// Following code draws all glyphs in Red color.
var glyphRun = ConvertTextLinesToGlyphRun(this.glyphTypeface, this.renderingEmSize, this.advanceWidth, this.advanceHeight, this.baselineOrigin, lines);
drawingContext.DrawGlyphRun(Brushes.Red, glyphRun);
}
return drawing;
}
static GlyphRun ConvertTextLinesToGlyphRun(GlyphTypeface glyphTypeface, double renderingEmSize, double advanceWidth, double advanceHeight, Point baselineOrigin, string[] lines)
{
var glyphIndices = new List<ushort>();
var advanceWidths = new List<double>();
var glyphOffsets = new List<Point>();
var y = baselineOrigin.Y;
for (int i = 0; i < lines.Length; ++i)
{
var line = lines[i];
var x = baselineOrigin.X;
for (int j = 0; j < line.Length; ++j)
{
var glyphIndex = glyphTypeface.CharacterToGlyphMap[line[j]];
glyphIndices.Add(glyphIndex);
advanceWidths.Add(0);
glyphOffsets.Add(new Point(x, y));
x += advanceWidth;
}
y += advanceHeight;
}
return new GlyphRun(
glyphTypeface,
0,
false,
renderingEmSize,
glyphIndices,
baselineOrigin,
advanceWidths,
glyphOffsets,
null,
null,
null,
null,
null);
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统诊断;
使用System.Windows;
使用System.Windows.Media;
使用System.Windows.Threading;
命名空间WpfApplication
{
公共部分类主窗口:窗口
{
Random rand=新的Random();
秒表;
长帧计数器=0;
字形字体字形字体;
双渲染EMSIZE、advanceWidth、advanceHeight;
点基线原点;
公共主窗口()
{
初始化组件();
新字体(“Consolas”).TryGetGlyphTypeface(去掉这个.glyphTypeface);
此.renderingEmSize=10;
this.advanceWidth=this.glyphtype.AdvanceWidths[0]*this.renderingEmSize;
this.advanceHeight=this.glyphtype.Height*this.renderingEmSize;
this.baselineOrigin=新点(0,this.glyphtype.Baseline*this.renderingEmSize);
CompositionTarget.Rendering+=CompositionTarget\u Rendering;
调度程序计时器=新调度程序();
timer.Interval=TimeSpan.From毫秒(1000);
timer.Tick+=定时器_Tick;
timer.Start();
}
void CompositionTarget_呈现(对象发送方,事件参数e)
{
如果(this.stopwatch==null)
this.stopwatch=stopwatch.StartNew();
++这个.帧计数器;
this.drawingImage.Drawing=this.Render();
}
字符串生成器域字符串(整数长度)
{
var chars=新字符[长度];
对于(int i=0;i10)
{
这个.stopwatch.Restart();
this.frameCounter=0;
}
}
私人绘图渲染()
{
变量行=新字符串[30];
对于(int i=0;i