c# GDI画图 双缓存画图分析

双缓冲绘图分析

 1、Windows 绘图原理 
  我们在 Windows 环境下看到各种元素,如菜单、按钮、窗口、图像,从根本上说,都是“画”出来的。这时的屏幕,就相当于一块黑板,而 Windows 下的各种 GDI 要素,如画笔、画刷等,就相当于彩色粉笔了。我们在黑板上手工画图时,是一笔一划的,电脑亦然。只不过电脑的速度比手工快的太多,所以在我们看起来好像所有的图形文字都是同时出现的。 
2、普通绘图方式的局限 
  上述绘图方式我们暂且称之为普通绘图方式吧。虽然这种方式能满足相当一部分的绘图需要,但是当要绘制的对象太复杂,尤其是含有位图时,电脑便力不从心了。这时的画面会显示的很慢,对于运动的画面,会给人“卡”住了的感觉,总之一个字:不爽。 
3、解决之道:双缓冲 
  双缓冲的原理可以这样形象的理解:把电脑屏幕看作一块黑板。首先我们在内存环境中建立一个“虚拟“的黑板,然后在这块黑板上绘制复杂的图形,等图形全部绘制完毕的时候,再一次性的把内存中绘制好的图形“拷贝”到另一块黑板(屏幕)上。采取这种方法可以提高绘图速度,极大的改善绘图效果。

  实现过程如下:

  1、在内存中创建与画布一致的缓冲区

  2、在缓冲区画图

  3、将缓冲区位图拷贝到当前画布上

  4、释放内存缓冲区

绘图示例:

  Winform应用程序中添加一个新的窗体;窗体中放置三个timer 分别使用原始画图模式、bitmap双缓画图模式、BufferedGraphicsContext双缓冲画图模式,Timer的Inteval设置为10;另外的三组按钮分别控制timer的开关;

引用的命名空间:

  using System.Drawing;

  using System.Drawing.Drawing2D;

DateTime t1 = DateTime.Now;
            Graphics g = this.CreateGraphics();
            LinearGradientBrush brush;
            if (flag)
            {
                brush = new LinearGradientBrush(new PointF(0.0f, 0.0f), new PointF(700.0f, 300.0f), Color.Blue, Color.Red);
                flag = false;
            }
            else
            {
                brush = new LinearGradientBrush(new PointF(0.0f, 0.0f), new PointF(400.0f, 300.0f), Color.Blue, Color.Red);
                flag = true;
            }
            for (int i = 0; i < 60; i++)
            {
                for (int j = 0; j < 60; j++)
                {
                    g.FillEllipse(brush, j * 10, i * 10, 10, 10);
                }
            }

            DateTime t2 = DateTime.Now;
            TimeSpan ts = t2 - t1;
            float per = 1000 / ts.Milliseconds;
            label1.Text = "速度:" + per.ToString() + "帧/s";

Timer1 基本画图模式

 DateTime t1 = DateTime.Now;
            Bitmap bmp = new Bitmap(600, 600);
            Graphics g = Graphics.FromImage(bmp);
            g.SmoothingMode = SmoothingMode.HighQuality;//画面呈现质量
            g.PixelOffsetMode = PixelOffsetMode.HighQuality;//像素偏移模式
            LinearGradientBrush brush;
            if (flag)
            {
                brush = new LinearGradientBrush(new PointF(0.0f, 0.0f), new PointF(700.0f, 300.0f), Color.Blue, Color.Red);
                flag = false;
            }
            else
            {
                brush = new LinearGradientBrush(new PointF(0.0f, 0.0f), new PointF(400.0f, 300.0f), Color.Blue, Color.Red);
                flag = true;
            }
            for (int i = 0; i < 60; i++)
            {
                for (int j = 0; j < 60; j++)
                {
                    g.FillEllipse(brush, j * 10, i * 10, 10, 10);
                }
            }
            this.CreateGraphics().DrawImage(bmp, 0, 0);//把画布贴到画面上
            DateTime t2 = DateTime.Now;
            TimeSpan ts = t2 - t1;
            float per = 1000 / ts.Milliseconds;
            label2.Text = "速度:" + per.ToString() + "帧/s";

Timer2 Bitmap方法绘图

  绘图过程:

1、在内存中建立一块“虚拟画布”:

  Bitmap bmp = new Bitmap(600, 600);

2、获取这块内存画布的Graphics引用:

  Graphics g = Graphics.FromImage(bmp);

3、在这块内存画布上绘图:

  g.FillEllipse(brush, 100, 100, 100, 100);

4、将内存画布画到窗口中

  this.CreateGraphics().DrawImage(bmp, 0, 0);

 DateTime t1 = DateTime.Now;
            BufferedGraphicsContext current = BufferedGraphicsManager.Current;//提供创建图形缓冲区的方法
            BufferedGraphics bg;
            bg = current.Allocate(this.CreateGraphics(), this.DisplayRectangle);
            Graphics g = bg.Graphics;
            LinearGradientBrush brush;
            if (flag)
            {
                brush = new LinearGradientBrush(new PointF(0.0f, 0.0f), new PointF(700.0f, 300.0f), Color.Blue, Color.Red);
                flag = false;
            }
            else
            {
                brush = new LinearGradientBrush(new PointF(0.0f, 0.0f), new PointF(400.0f, 300.0f), Color.Blue, Color.Red);
                flag = true;
            }
            for (int i = 0; i < 60; i++)
            {
                for (int j = 0; j < 60; j++)
                {
                    g.FillEllipse(brush, j * 10, i * 10, 10, 10);
                }
            }

            bg.Render();
            bg.Dispose();
            DateTime t2 = DateTime.Now;
            TimeSpan ts = t2 - t1;
            float per = 1000 / ts.Milliseconds;
            label3.Text = "速度:" + per.ToString() + "帧/s";

Timer3 BufferedGraphicsContext双缓冲绘图

  绘图过程:

1、获得对 BufferedGraphicsContext 类的实例的引用。 
2、通过调用 BufferedGraphicsContext.Allocate 方法创建 BufferedGraphics 类的实例。

3、通过设臵 BufferedGraphics.Graphics 属性将图形绘制到图形缓冲区。 
4、当完成所有图形缓冲区中的绘制操作时,可调用 
BufferedGraphics.Render 方法将缓冲区的内容呈现到与该缓冲区关联的绘图图面或者指定的绘图图面。

5、完成呈现图形之后,对 BufferedGraphics 实例调用释放系统资源的 Dispose 方法。

绘图效果:

上述的例子中可以看出

  基本的画图模式中会明显的感觉到画面从上到下依次刷新,刷新speed为1~2帧/s  图像刷新速度很慢

  使用Bitmap绘图模式 看到的画面是在不停的闪烁,刷新speed为13~14帧/s  图像刷新速度加快很多

  使用BufferedGraphicsContext双缓冲模式 看到画面闪烁更快,刷新speed为27~28帧/s  图像刷新速度最快

其他绘图注意点:

>>GraphicsPath 将相邻的点连接成线

Graphics g = this.CreateGraphics();
System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
            for (int i = 0; i < lstPoint.Count - 1; i++)
            {
                path.AddLine(new Point(lstPoint[i], dicPoint[lstPoint[i]]), new Point(lstPoint[i + 1], dicPoint[lstPoint[i + 1]]));//示例中添加的是前后两个点位的x、y坐标
            }
            SolidBrush brush=new SolidBrush (c_Line);
            g.DrawPath(new Pen(brush), path);

GraphicsPath

>>Graphics.DrawCurve可以将两条直线的连接部位做平滑处理

Graphics g = this.CreateGraphics();
Point[] array_Point = new Point[lstPoint.Count];
            for (int i = 0; i < lstPoint.Count; i++)
            {
                array_Point[i].X = lstPoint[i];
                array_Point[i].Y = dicPoint[lstPoint[i]];
            }
            SolidBrush brush=new SolidBrush (c_Line);
            g.DrawCurve(new Pen(brush), array_Point);    

DrawCurve

时间: 2024-10-08 22:00:34

c# GDI画图 双缓存画图分析的相关文章

C#-gdi画图,双缓冲画图,Paint事件的触发---ShinePans

在使用gdi技术画图时,有时会发现图形线条不够流畅,或者在改变窗口大小时会闪烁不断的现象.(Use DoubleBuffer to solve it!)                                                                                                                                                                              

简单的GDI+双缓冲的分析与实现

为什么要使用双缓冲绘制 在进行多图元绘制的时候: 因为是要一个一个画上去,所以每画一个图元,系统就要做一次图形的绘制操作,图形的重绘是很占用资源的,特别当需要重绘的图形数量很多的时候,所造成的消耗就特别大,导致闪烁,不流畅等情况.那么如何来解决这个问题呢? 那就是双缓冲. 它的基本原理就是 先在内存中开辟一块虚拟画布,然后将所有需要画的图元一个个先画在这块“虚拟画布”上,最后在一次性将整块“虚拟画布”画到真正的窗体上.因为所有的单个图形的绘制都不是真正的调用显示系统来“画”,所以不会占用显示系统

GDI+ 双缓存 和 刷新桌面(F5)

GDI+双缓存 POINT currentPoint; GetCursorPos(&currentPoint); HWND hWnd = ::GetDesktopWindow(); int nWidth = GetSystemMetrics(SM_CXSCREEN); int nHeight = GetSystemMetrics(SM_CYSCREEN); RECT r; GetWindowRect(hWnd, &r); Bitmap bmp(nWidth, nHeight); Graph

Win32双缓冲画图原理

网上有许多文章讲述了如何使用Visual C++程序实现双缓冲,都是用C++面向对象语言写的,可能对很多没有接触过面向对象语言的C语言初学者来说理解起来有些困难,并且有些好心人也只是把源代码贴上去,不做注释,这就使读者读起来更费劲了.    在这里,我会就每一条语句作出解释.其中有一个地方比较有趣,值得讨论(见下文).好了,我们首先看一下双缓冲的基本原理: 一.双缓冲原理及图解 (1)定义设备描述表及位图句柄    HDC hMemDC;    HBITMAP hBitmap;(2)创建一个与窗

关于双缓存

为了防止屏幕闪烁现象,利用双缓存解决.原理主要是将图片画在虚拟屏幕上,再将画直接从虚拟屏幕上直接画在实际屏幕上.repaint()方法实际上是先调用update()方法然后再调用paint()方法,实现双缓存就利用了这一点,代码如下 Image offScreenImage=null; public void update(Graphics g)       {         if(offScreenImage==null)             offScreenImage=this.cr

Android中用双缓存技术,加载网络图片

最近在学校参加一个比赛,写的一个Android应用,里面要加载大量的网络图片,可是用传统的方法图片一多就会造成程序出现内存溢出而崩溃.因为自己也在学习中,所以看了很多博客和视频,然后参照这些大神的写源码,自己写了一个加载网络图片工具类. 里面要用到一个经典的图片缓存库DiskLruCache 下载地址为:  DiskLruCache下载 下面是使用这个类实现的 双缓存网络图片加载 [java] view plain copy public class DiskLruCacheUtils { pr

android listview 异步加载图片并防止错位+双缓存

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite

Android 框架修炼-自己封装双缓存管理框架库

一.概述 Android开发中,网络请求是很重要的一部分,而缓存网络请求来的图片或者响应结果字符串或者结果流,既可以省流量,同时也可以帮助我们 解决无网或弱网情况下加载情况,当然也可以提升程序性能效率.纵所周知,缓存管理中肯定需要用到内存缓存,这里我们采用LruCache来管理内存的缓存. LruCahce虽然速度快,但是只是内存级别的缓存,为了实现持久化的缓存,我们还需要文件级别的缓存,也就是说我们要把缓存保存到文件,而文件则是保存 到手机存储或者SD卡存储中,即实现Disk级别的缓存,这里我

C#-gdi绘图,双缓冲绘图,Paint事件的触发---ShinePans

在使用gdi技术绘图时,有时会发现图形线条不够流畅,或者在改变窗体大小时会闪烁不断的现象.(Use DoubleBuffer to solve it!)