WPF C# .NET 6.0中Graphics绘文本致性能下降的解决方法
问题描述
在WPF C# .NET 6.0环境中,backbuffer(WritableBitmap)已包含OpenTK绘制的线条数据,单独渲染这些线条时流畅度正常,但通过System.Drawing.Graphics绘制大量文本后,整体流畅度明显下降。相关代码如下:
var bm2 = new System.Drawing.Bitmap((int)backbuffer.Width, (int)backbuffer.Height, backbuffer.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, backbuffer.BackBuffer); backbuffer.Lock(); using (graphics = System.Drawing.Graphics.FromImage(bm2)) { graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; // 实际场景中会调用数千次DrawString,此处仅为示例 graphics.DrawString("MyText test only up5", new Font("Tahoma", 14), System.Drawing.Brushes.White, 10, (float)(OtkCanvas.otkHeight - 60)); } backbuffer.Unlock(); OtkCanvas.imageSource = backbuffer;
请问如何解决这种因Graphics绘制文本导致的性能下降问题?
优化方案
1. 复用Font和Brush对象
每次调用DrawString时创建新的Font和Brush会产生大量不必要的内存分配与GC开销,提前初始化并复用这些对象:
// 类级别成员,仅初始化一次 private readonly Font _tahomaFont = new Font("Tahoma", 14); private readonly Brush _whiteBrush = System.Drawing.Brushes.White; // 绘制时直接复用 graphics.DrawString("MyText test only up5", _tahomaFont, _whiteBrush, 10, (float)(OtkCanvas.otkHeight - 60));
2. 降低图形质量设置
当前启用的HighQuality级别的合成、插值和像素偏移模式会大幅增加CPU计算量。若文本显示质量可接受,改为性能优先的设置:
graphics.CompositingQuality = CompositingQuality.HighSpeed; graphics.InterpolationMode = InterpolationMode.Low; graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
3. 修正WritableBitmap锁定时机
原代码中backbuffer.Lock()的时机错误,应在创建Bitmap前就锁定,且缩短锁定时长,仅在必要操作时保持锁定:
backbuffer.Lock(); try { var bm2 = new System.Drawing.Bitmap((int)backbuffer.Width, (int)backbuffer.Height, backbuffer.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, backbuffer.BackBuffer); using (var graphics = System.Drawing.Graphics.FromImage(bm2)) { // 执行绘制操作 } } finally { backbuffer.Unlock(); }
4. 替换GDI+为WPF原生文本渲染
System.Drawing基于GDI+,与WPF的DirectX渲染管道不兼容,跨管道绘制会产生额外开销。推荐两种替代方案:
方案A:用FormattedText直接绘制到WritableBitmap
利用WPF原生的DrawingVisual和FormattedText完成文本渲染,避免GDI+交互:
backbuffer.Lock(); try { var visual = new DrawingVisual(); using (var drawingContext = visual.RenderOpen()) { var formattedText = new FormattedText( "MyText test only up5", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Tahoma"), 14, Brushes.White); drawingContext.DrawText(formattedText, new Point(10, OtkCanvas.otkHeight - 60)); } // 将DrawingVisual内容渲染到WritableBitmap var renderTarget = new RenderTargetBitmap((int)backbuffer.Width, (int)backbuffer.Height, 96, 96, PixelFormats.Pbgra32); renderTarget.Render(visual); // 按需将renderTarget内容复制到backbuffer(需处理像素格式转换) } finally { backbuffer.Unlock(); }
方案B:叠加WPF文本元素
直接在OpenTK画布的Image控件上方放置TextBlock或Label,让WPF原生负责文本渲染与布局,完全规避GDI+操作:
<Grid> <Image x:Name="OtkCanvasImage" Source="{Binding imageSource}"/> <TextBlock Text="MyText test only up5" FontFamily="Tahoma" FontSize="14" Foreground="White" Margin="10,0,0,60" VerticalAlignment="Bottom"/> </Grid>
5. 批量处理文本绘制
若需绘制数千条文本,尽量一次性批量处理:统一设置StringFormat的对齐、换行等参数,减少重复配置;或者合并相同样式的文本绘制逻辑,降低DrawString调用次数。
内容的提问来源于stack exchange,提问作者Shree John
相关产品推荐
相关产品推荐

