c#如何实现连续移动

pink-zhu 2016-05-13 10:20:39
楼主最近有一个项目方案无从下手,希望大家帮帮忙
具体是这样的:要在显示界面上实现一个图形的连续移动,背景是自己绘制并且不定期局部刷新,显示的图形为自定义的折线段和文字。接收位置信息之后,进行图形的实时绘制,要求每秒处理100~1000条数据。
原本的方案是这样的:使用双层画布,一个绘制背景,一个绘制图形(图形的背景设为透明),当背景或图形位置变化时,将需要刷新区域的背景和图形调用DrawImage()函数,叠加到显示界面。
这样是可以实现连续移动的,但是无法达到每秒处理100~1000条数据的要求;因为DrawImage函数需要时间,导致CPU占用率也很高。
请大神们帮帮忙。
...全文
715 33 打赏 收藏 转发到动态 举报
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhoujk 2016-05-22
  • 打赏
  • 举报
回复
现在的速度是多少? 找出瓶颈,如果是在IO,就只传送有效数据来覆盖原来的数据。如果是CPU,可以尝试一下并行。把数据分成N个块,然后在N个核里分别绘制,最后拼接出一个完整的画面交货。
xuzuning 2016-05-17
  • 打赏
  • 举报
回复
之所以称之为背景,那么他就是相对不变的 所以可以放在 PictureBox.Image 中,也就不会被 Graphics.DrawXXXX 破坏掉 你动态绘制的都是前景,虽然有些部分是属于背景的,但因为是动态绘制的,所以仍应归于前景 你本来就是全部绘制的,只不过只在其他地方画好,再复制过来 如果你直接绘制,不就可以省去复制、粘贴(DrawImage)的过程了吗? 你都可以复制、粘贴了,就证明画图本身并没有问题,所以直接画也不会有问题 Graphics.Clip 是绘制时的裁剪区域,你可通过他缩小实际绘制时的区域 扫描该区域涉及到那几个轨道 这的确是个问题,如果你硬是逐像素看短的话,肯定是不可取的,但你可以将轨道保存到 GraphicsPath 中,通过 IsVisible 方法就可轻松的找到点是否在路径中
足球中国 2016-05-17
  • 打赏
  • 举报
回复
你可以打印一下绘制函数所用的时间。看看哪个费时把哪个优化下。
pink-zhu 2016-05-17
  • 打赏
  • 举报
回复
引用 16 楼 xuzuning 的回复:
我还以为你是在写游戏呢,原来是这么加单的示意图 你把是事先设计好的轨道图放在 path 里,和列车一起画就是了(包括轨道上的色块)
因为背景图很大,每次改变的区域很小,所以使用的是局部绘制,自定义控件实现界面的局部刷新。如果背景需要改变,则获取该部分区域的位置,调用graphics.Clear()清除之后重绘,清除过程会将相应位置的蓝色折线覆盖,所以要将该区域的蓝色折线复制过来,复制使用的是DrawImage函数。如果要一起画的话,就要先扫描该区域涉及到那几个轨道才行,当背景图很复杂时,扫描会很慢啊。所以只能复制过去。目前CPU增加定位到了DrawImage函数这边,DrawImage函数频繁调用时运行时间和CPU占用都很大,远远高出预期。所以这个方法目前不太可行。所以希望大神们能给点新思路。我最近只局限在现在这种多重画布复制叠加的模式中了,思维定式。请大神点拨点拨。
pink-zhu 2016-05-17
  • 打赏
  • 举报
回复
引用 20 楼 zanfeng 的回复:
[quote=引用 15 楼 baidu_28217989 的回复:] [quote=引用 14 楼 xuzuning 的回复:] 就是嘛,你的做法是有问题的 1、其实你的背景并没有变,只是因为擦除的部分区域,所以才需要补上 2、实时数据产生的图形不是实时绘制的,而是绘制在备份上复制过来的 这就是说,你完成一次更新,需要 擦除,复制背景,画备份,复制备份 这四个步骤 其实 作为背景的图片放在 PictureBox.Image 中是不会被清除掉的,哪怕被其他窗口覆盖过了 绘图应在 Paint 事件中完成。当 Paint 事件到来时 PictureBox 的所有绘图都会被自动消除(除了 Image) 你只需重新绘制就可以了。新旧数据按次序保存在一个序列里,绘制时只取需要的一段(只要每次的起点不同,自然就动起来了)
背景并不是固定不变的, 如图,轨道即为背景图,蓝色线段则是实时图形。背景图也是需要变化的,比如图中的棕色部分就是根据数据重绘的[/quote] 初看如果这个效果实现不是挺容易的。 你的东西描述的还是不够细,别人给的意见也给不到你的需求里。[/quote] 是需要每次改变背景或者蓝色折线段。但是为了防止擦除背景将蓝色折线擦除,所以在背景重绘之后,要将相应位置的蓝色折线段用DrawImage复制过去才可以。因为清除使用的是graphics.Clear(Color.Black);会将蓝色折线段覆盖掉,所以要进行一次复制。因此,要将背景和蓝色折线都复制到总画布上。PS:有三重画布,背景画布(背景为黑色),蓝色折线段画布(背景透明),和总画布(背景黑色)。所以每一次的数据改变都需要:一次绘制和两次复制才可以,当数据传输很快时,CPU占用率很高。CPU占用不符合目标。但是目前想不出更好的了,所以希望大神们给点思路。目前我的思路只局限于此,想不出更好的了。
pink-zhu 2016-05-17
  • 打赏
  • 举报
回复
引用 21 楼 guonan198811 的回复:
楼主看看WPF依赖属性和绑定的相关知识,以及MVVM模式,WPF的设计思想其中其中很重要的一个就是解决界面和数据的交互问题。靠整体重绘是不可能实现大量的数据交互的。
并没有整体重绘啊,是局部重绘和局部擦除。根据实时数据获取需要改变的区域并进行重绘和界面刷新,刷新也是自定义控件实现局部刷新的。测了一下,DrawImage函数所用CPU最多,但是目前只想到多层画布叠加的方法,因为c#没有异或绘图,为了防止绘制蓝色线段时擦除了背景,只能把相应位置的背景再复制一遍,所以一次更新,需要一次绘制和两次DrawImage才可以,CPU占用率很高,当背景比较大,数据传输较快时,很容易卡死。
pink-zhu 2016-05-17
  • 打赏
  • 举报
回复
引用 30 楼 yzxdc 的回复:
楼主完全是想多了,根本不用考虑部分的问题,就是整个图像刷新。写一个特定时刻画出整个图像的方法,然后大致估计一下调用这个方法的事件(用datetime记录在日志上),取一个平均值,然后用一个计时器每隔0.02秒调用一次刷新界面就成了,也就是50帧
目前的重点并不是刷新,而是数据传输速度远小于绘制和复制的时间。刷新并没有问题,是绘制及复制的时间过长,CPU占用率过高。
pink-zhu 2016-05-17
  • 打赏
  • 举报
回复
目前,在思考是否可以使用异或的方法,直接绘制图形,这样就不用担心擦除时会影响到其他图形。但是在c#下没有找到异或绘图的函数,调用API的函数的话,绘制时间很长,一条线段就需要几十毫秒,远远超过数据传输时间。请问,是否有效率更高得而异或绘图的方法,在c#中能够使用的?
yzxdc 2016-05-17
  • 打赏
  • 举报
回复
楼主完全是想多了,根本不用考虑部分的问题,就是整个图像刷新。写一个特定时刻画出整个图像的方法,然后大致估计一下调用这个方法的事件(用datetime记录在日志上),取一个平均值,然后用一个计时器每隔0.02秒调用一次刷新界面就成了,也就是50帧
pink-zhu 2016-05-17
  • 打赏
  • 举报
回复
引用 27 楼 zhoujk 的回复:
你不用管数据的刷新率,只定你的软件刷新率就行了,也就是说,不是数据的刷新来引发界面的刷新,是用另一个计时器来实现。 每个点,读取当前的数据状态,然后将有变化的部分绘制出新的图像并且覆盖在指定区域即可。 计时器的速度楼上已经说了,电视、电影都是30FPS左右即可。也就是说,如果你的绘制和刷新速度小于 1/30",那软件肯定没问题。
目前只这样处理的:用定时器设定界面刷新次数,但是数据、绘制照常进行,将刷新区域region.Union()叠加,到刷新的时候一起刷新修改的这一region区域。具体都已经实现,但是目前方案用了多重画布,每次都要绘制、复制,比较麻烦,CPU占用率比目标高出很多,所以,要换方案。
zhoujk 2016-05-17
  • 打赏
  • 举报
回复
在 Graphic 上 DrawLine( ) 肯定没问题。不会有不连续的跳跃感。 如果数据太多,为了提速还有更加变态的办法,就是尽量减少重绘的区域。你把图形文件分成N个方格。然后在计时器事件上算出哪些方格是有变化的。删除这些方格,然后重绘和它有关的所有数据。这是比较简单的方法。如果速度还不够,就把每个矢量数据分割到这些方格里,每次要更新的时候只画方格里的内容就行。例如,将一条背景上穿过三个方格的线段分割成三条线段,当某个方格需要重绘时,只需绘制它对应的那条线段,而不是整条线段。这样做的好处是速度快,但是代码难度太大。
zhoujk 2016-05-17
  • 打赏
  • 举报
回复
你不用管数据的刷新率,只定你的软件刷新率就行了,也就是说,不是数据的刷新来引发界面的刷新,是用另一个计时器来实现。 每个点,读取当前的数据状态,然后将有变化的部分绘制出新的图像并且覆盖在指定区域即可。 计时器的速度楼上已经说了,电视、电影都是30FPS左右即可。也就是说,如果你的绘制和刷新速度小于 1/30",那软件肯定没问题。
南天空 2016-05-16
  • 打赏
  • 举报
回复
楼主看看WPF依赖属性和绑定的相关知识,以及MVVM模式,WPF的设计思想其中其中很重要的一个就是解决界面和数据的交互问题。靠整体重绘是不可能实现大量的数据交互的。
jwh2004 2016-05-16
  • 打赏
  • 举报
回复
帮顶,我只知道要想快速处理图片数据,最好在内存中处理数据,用LockBits将BitmapData数据放内存中处理,参考: http://msdn.microsoft.com/zh-cn/library/5ey6h79d(v=vs.110).aspx
足球中国 2016-05-16
  • 打赏
  • 举报
回复
引用 15 楼 baidu_28217989 的回复:
[quote=引用 14 楼 xuzuning 的回复:] 就是嘛,你的做法是有问题的 1、其实你的背景并没有变,只是因为擦除的部分区域,所以才需要补上 2、实时数据产生的图形不是实时绘制的,而是绘制在备份上复制过来的 这就是说,你完成一次更新,需要 擦除,复制背景,画备份,复制备份 这四个步骤 其实 作为背景的图片放在 PictureBox.Image 中是不会被清除掉的,哪怕被其他窗口覆盖过了 绘图应在 Paint 事件中完成。当 Paint 事件到来时 PictureBox 的所有绘图都会被自动消除(除了 Image) 你只需重新绘制就可以了。新旧数据按次序保存在一个序列里,绘制时只取需要的一段(只要每次的起点不同,自然就动起来了)
背景并不是固定不变的, 如图,轨道即为背景图,蓝色线段则是实时图形。背景图也是需要变化的,比如图中的棕色部分就是根据数据重绘的[/quote] 初看如果这个效果实现不是挺容易的。 你的东西描述的还是不够细,别人给的意见也给不到你的需求里。
KJ_Wang 2016-05-13
  • 打赏
  • 举报
回复
多种情况试呀!
xuzuning 2016-05-13
  • 打赏
  • 举报
回复
我问的是:数据在画面上是怎样呈现的?是文字,图案还是图片? 你总提到 DrawImage,想必是图片吧? 假定的的绘图容器是 PictureBox,那么改变 PictureBox.Image 就更换了背景。背景一般变化不频繁,弄一些全局的 Image 做备份就可以了,不必每次都加载或绘制 数据图片可放在各自的 PictureBox 中,移动只是改变一下 Location 属性,并不需要重绘 如有背景透明的需求,那也只需将 Parent 指向作为背景图的 PictureBox 就可以了
pink-zhu 2016-05-13
  • 打赏
  • 举报
回复
引用 5 楼 crystal_lz 的回复:
电影都还没有 每秒100-1000帧呢。。。。
100~1000条数据并不是指每秒100~1000帧,连续移动的速度并不是很快,关键是数据大部分都是背景的局部刷新信息,频繁的局部刷新背景,每次刷新就要将两个画布叠加,使用drawImage的频率很高,效率很低。
失落的神庙 2016-05-13
  • 打赏
  • 举报
回复
视频弹幕?
crystal_lz 2016-05-13
  • 打赏
  • 举报
回复
电影都还没有 每秒100-1000帧呢。。。。
加载更多回复(12)

110,533

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧