Windows 窗体:如何有效地处理重绘?
我有一个带有图片框的表单,它显示图片和在图片框的 Paint 事件处理程序中绘制的一些叠加层。叠加层根据鼠标移动更新自身(基本上改变叠加层不同部分的不透明度)。
目前,我正在从鼠标移动处理程序调用 pictureBox.Invalidate()
以确保重新绘制覆盖层。我还实现了一些逻辑来确定是否确实需要重绘 - 如果没有对象随着鼠标移动而改变其不透明度,则 PictureBox
不会失效。
当我移动鼠标的速度比非常慢时,我在双核计算机上的 CPU 使用率仍然是 50% - 我猜测绘画例程无法像鼠标移动事件那样频繁地重新绘制生成的。
绘制的对象不多,最多 10 个填充矩形,每个矩形有 4 个填充三角形角。问题已经存在于单个覆盖对象上。基本上只有 FillRectangle
和 FillArea
方法用于执行绘制。
在这种情况下,您会建议采取什么方法来防止如此高的 CPU 使用率?
I have a form with a picturebox which displays a picture and some overlay drawn in the Paint event handler of the picturebox. The overlay updates itslef (basically changes opacity of different parts of the overlay) based on mouse movement.
Currently, I am calling pictureBox.Invalidate()
from mouse moved handler to ensure that the overlay is repainted. I have also implemented some logic to determine whether the repaint is really needed - if no object changed its opacity with the mouse move, the PictureBox
is not invalidated.
I am still getting 50 percent CPU usage on a dual core machine when I move the mouse faster than very slowly - I am guessing the painting routine just does not manage to repaint as often as the mouse moved events are generated.
There are not many objects drawn, up to 10 filled rectangles with a 4 filled triangle corners each. The problem is there with a single overlay object already. Basically only FillRectangle
and FillArea
methods are used to perform the painting.
What approach would you propose in this situation to prevent such high CPU usage?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
首先,弄清楚您实际上需要重新绘制的频率才能获得您想要的效果。现在,您正在重绘以响应鼠标事件,但这些事件可能比您想象的要多很多,而且您可能不需要对每一个事件都进行重绘。绘画(响应Invalidate())在大多数情况下都是低优先级的,但这只是意味着您最终将使用任何备用 CPU 来完成它 - 最好跟踪上次重新粉刷的时间,避免过早再次粉刷。
使用计时器将刷新率固定在某个常数(从大约 25hz 刷新率开始有 40 毫秒的延迟,然后根据需要增加或减少)是一种简单的方法来做到这一点...计时器 (System.Windows.Forms.Timer至少)也是低优先级的,因此您不必担心刷新逻辑会抢占更重要的事件处理程序。
当然,请保留您用来确定是否有任何实际更改的现有代码。当它出现时设置一个标志,当未设置该标志时,只需在计时器事件处理程序中不执行任何操作即可。
完成此操作后,您应该会看到所使用的最大处理时间立即减少,因为您将刷新率与鼠标速率分离。您还会发现您可以更好地控制中值处理时间,因为刷新率在您的控制之下:太高,降低计时器滴答率;太高,则降低计时器的滴答率;太高,则降低计时器的滴答率。不够流畅,增加!
Well, for starters, figure out how often you actually need to repaint in order to get the effect you're looking for. Right now, you're redrawing in response to mouse events, but there can be a lot more of those than you might think, and you probably don't need to redraw for each and every one of them. Painting (in response to Invalidate()) is low-priority for the most part, but that just means you'll end up using up any spare CPU to do it - better that you keep track of the time you last repainted and avoid doing it again too soon.
Using a timer to fix the refresh rate at some constant (start with a 40ms delay for a roughly 25hz refresh rate, and increase or decrease as needed) is an easy way to do this... Timers (System.Windows.Forms.Timer at least) are also low-priority, so you don't really have to worry about your refresh logic preempting more important event handlers.
Of course, keep the existing code you're using to determine whether or not anything actually changed. Set a flag when it does, and when that flag is not set, simply do nothing in your timer event handler.
With this done, you should see an immediate decrease in the maximum processing time used, as you'll have decoupled the refresh rate from the mouse rate. You'll also find that you have more control over the median processing time, since the refresh rate is under your control: too high, decrease the timer tick rate; not smooth enough, increase it!
限制绘制周期。
例如,在游戏编程中,典型的帧速率是
30-60fps
或每秒三十到六十帧。这意味着屏幕每秒绘制 30-60 次。也对您的应用程序应用某种限制。12fps
大致是人眼被欺骗而相信是动画的最低帧速率,因此我不会低于此值。老实说,鼠标移动似乎是错误的。例如,快速向鼠标发送垃圾邮件会增加抽取率。
Limit the draw cycles.
For example in game programming a typical framerate is
30-60fps
or thirty to sixty frames per second. That means that the screen is drawn from 30-60 times a second. Apply some sort of limit to your application too.12fps
is roughly the lowest frame rate the the human eye is tricked into believing is animation, so I wouldn't go below this.On mouse moves seems wrong to be honest. For example, spamming the mouse rapidly would increase the draw rate.
如果您在鼠标移动事件中重绘,那么您将重绘很多次。问题是,您真的需要它在每次鼠标实际移动时绘制,还是希望它在移动完成时绘制?或者介于两者之间?添加一些仅在鼠标移动结束时重绘的逻辑,或类似的效果。
If you're redrawing in the mouse moved event, then you'll be redrawing a lot. The question is, do you really need it to draw every time the mouse is actually moved, or do you want it to draw when it's finished moving? Or somewhere in between? Add some logic that only redraws at the end of the mouse movement, or something to that effect.