如何在WinForms中提高绘图性能?

hkmswyz6  于 2023-10-23  发布在  其他
关注(0)|答案(2)|浏览(125)

我知道Winforms不是为大量图形使用而设计的,但我不知道C#以外的任何东西,所以多年来我使用C#和Visual Studio创建了各种程序,这些程序将事情推向了Visual Studio / Winforms限制的边缘。
Example view of the program in question
该程序读取摇滚乐队格式的文件,并像音乐播放器一样播放它们,其附加功能是利用摇滚乐队文件中的附加信息,如图表,专辑艺术和歌词来完成体验。一切都很好。绘图是在图形上完成的,所有这些,没有图像被加载和移动(我发现这是较慢的)。
每个图形示例都伴随着我在这里找到的以下代码:

graphics.SmoothingMode = SmoothingMode.HighSpeed;
graphics.CompositingQuality = CompositingQuality.HighSpeed;

我被引导相信这会有助于表现。
所有的绘制都是基于一个以60 Hz(16 ms)运行的计时器,因此每秒60次,它会在屏幕上绘制所有内容。这对所有的事情都有效,除了歌词。我可以有歌词在静态模式或卡拉OK模式没有问题。但是如果我把它们设置为与声乐音符一起滚动(在上面的图像中,底部的蓝色音符,歌词应该与这些音符对齐并与它们一起沿着),除非你暂停,否则不可能阅读它们,那么它是非常清晰的。我试过将计时器的刷新率从1 ms调整到500 ms,但行为没有改变。歌词难以辨认。
这是滚动歌词的相关代码:

private void DrawLyricsScrolling(IEnumerable<Lyric> lyrics, Control label, Color color, Graphics graphics)
{
        if (!openSideWindow.Checked || PlayingSong == null || btnPlayPause.Tag.ToString() == "play" || !doScrollingLyrics) 
            return;

        var time = GetCorrectedTime();
        label.Text = "";

        graphics.SmoothingMode = SmoothingMode.HighSpeed;
        graphics.CompositingQuality = CompositingQuality.HighSpeed;

        using (var pen = new SolidBrush(MediaPlayer.Visible ? picVisuals.BackColor : LabelBackgroundColor))
        {
            graphics.FillRectangle(pen, label.ClientRectangle);
        }

        foreach (var lyric in lyrics.TakeWhile(lyric => lyric.LyricStart <= time + PlaybackWindow).Where(lyric => !(lyric.LyricStart + (PlaybackWindow * 2) < time)))
        {
            var left = (int)(((lyric.LyricStart - time) / PlaybackWindow) * label.Width);
            TextRenderer.DrawText(graphics,lyric.LyricText,label.Font,new Point(left, 0),color);
        }
}

我希望你能指出我可能做错了什么,或者我可能完全错过了什么,以提高性能,如果可能的话,在Winforms的范围内。我在这个程序中有超过6,000行独特的代码,所以我不希望更改为WPF或其他语言或环境。它可以在C# / Winforms中得到改进,或者保持这种方式。
谢谢你,谢谢
编辑:
https://i.imgur.com/vZrpT5F.png
如果我禁用所有其他渲染,只有滚动歌词,他们仍然滚动滞后,不容易阅读。这张截图是用软件自己的F12截图功能拍摄的。它停止了动作,看起来很完美。如果我用Print Screen截图,同样的事情。我想我不能复制录制视频的模糊/滞后。
我在某个地方发现了使用foreach比使用List慢得多的评论。所以我把密码改成

for (var i = 0; i < lyrics.Count(); i++)
        {
            if (lyrics[i].LyricStart < time) continue;
            if (lyrics[i].LyricStart > time + PlaybackWindow) return;
            var left = (int)(((lyrics[i].LyricStart - time) / PlaybackWindow) * label.Width);
            TextRenderer.DrawText(graphics, lyrics[i].LyricText, label.Font, new Point(left, 0), color);
        }

在行为上没有区别。此外,有人建议使用周期性计时器,但这是在.NET 6+上,这个程序是在4.8.1上,所以我没有访问周期性计时器。将继续努力。

gpfsuwkq

gpfsuwkq1#

我们在这里可以看到一些东西

foreach (var lyric in lyrics.TakeWhile(lyric => lyric.LyricStart <= time + PlaybackWindow).Where(lyric => !(lyric.LyricStart + (PlaybackWindow * 2) < time)))
{
    var left = (int)(((lyric.LyricStart - time) / PlaybackWindow) * label.Width);
    TextRenderer.DrawText(graphics,lyric.LyricText,label.Font,new Point(left, 0),color);
}

从这段代码和你所展示的图像中,我理解的是,你是:
1/循环歌词,逐字逐句,直到当前时间窗口为止
2/然后你删除那些太早/应该滚动到非视图
3/然后你把这些话,一个接一个地
无论是LinQ还是逐字渲染都不是很高效,尤其是每秒渲染60次。我的建议是:一次性渲染整个(或几分钟的)歌词,并“滑动”整个渲染文本句子

mpgws1up

mpgws1up2#

您可以尝试使用EmguCV,它是OpenCV(用C++编写的机器视觉库)的C# .net Package 器,其中包括内置的在图像上绘制文本/形状的函数。
NuGet的库是:如果Emgu.CV.runtime.windows这与你有关,请发表评论,我会在这个问题上再加几个指针。

相关问题