为什么我用双缓冲技术了,绘图还是有闪烁,用的GDI+

C语言游戏编程:GDI怎么实现双缓冲绘图去掉闪烁 - 为程序员服务
为程序员服务
推荐团队博客
相关专栏文章
最近更新博客
C语言游戏编程:GDI怎么实现双缓冲绘图去掉闪烁
在上篇文章中将我要用,使用的是GDI的绘图模式(目前正在移植到DX上去,想添加一些更好友好的动画)。数据与动画分离,动画的帧率保持在30左右。但是绘图的时候画面出现了强烈的闪动。
排查了一下原因,不是由于电脑性能的原因(如果画几个线就闪的话那就糟了),而是由于采用的动画方式导致的。我采用的是擦除,绘制,擦除,绘制的模式。没有使用双缓冲。要解决画面闪烁,就要使用到双缓冲。
双缓冲指的是在创建两个一样大小、一样色深的表面。一个表面供你显示,一个供你作图,轮流交换。这样每次就可以直接扫描画好的表面到屏幕就OK了,不会出现闪烁的情况。更多双缓冲的知识你可以谷歌一下:)
使用DX的双缓冲非常容易,因为其内置了双缓冲的良好接口。但是怎么在GDI下使用双缓冲呢?这是个问题。最后在谷歌上找到了答案。
就是使用这几个函数, , 。
第一个函数在内存中创建一个单色的显示表面作为后备缓冲,第二个函数将你创建的后备缓冲设置成和你窗口设备环境一样大小、一样色深的表面,作为后备表面。第三个函数将后备缓冲的内容拷贝到显示表面中去。
下面是一个实现GDI下双缓冲的代码:
** current_hdc为当前的窗口的hdc 使用GetDC得到
** buffer_hdc 为后备缓冲 使用CreateCompatibleDC创建
HDC current_hdc,buffer_
current_hdc = GetDC(window_handle);
** 初始化工作
** 将buffer_hdc创建为和当前窗口hdc一样大小和一样色深
** 记得要使用SelectObject函数
** 该函数将CreateCompatibleBitmap创建的bmp
** 设置到buffer_hdc中去,使得和current_hdc一样大小
** 一样色深
buffer_hdc = CreateCompatibleDC(NULL);
HBITMAP bmp = CreateCompatibleBitmap(current_hdc,WINDOW_WIDTH,
WINDOW_HEIGHT);
SelectObject(buffer_hdc,bmp);
/* ... 其他的代码 */
/* 绘图 在buffer_hdc后备缓冲中绘制 */
LineTo(buffer_hdc,100,100);
** 将后备缓冲的内容显示到窗口
** 使用bitblt函数
BitBlt(current_hdc,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,
buffer_hdc,0,0,SRCCOPY);
/* 至此整个GDI下的双缓冲就实现了 */
文章中可能写得不是特别详细。下面是一个简单的示例程序,使用双缓冲在窗口上画几条直线,你可以试试不使用双缓冲,看看那是怎么样的效果。
当然你还有疑问可以联系我:大笨兔
若非注明,均为原创文章,转载请注明: 转载自
本文链接地址:
相关文章推荐:1127人阅读
写在前面:这两天在分析DUILIB代码,遇到GDI绘图,现在对内存绘图和双缓冲绘图还不是很清楚,写转两篇文章以留备用,等学好了再给大家讲吧。
转载地址:
首先理解内存绘图,即把要绘制的东西先在内存中画好,然后一次性的画到屏幕上来。内存绘图经常用来防止闪烁。因为闪烁的原因是因为反差太大。例如你的绘图过程是先用白色擦除整个窗口,然后再将黑色的文字画到屏幕上来,这样在窗口重绘的时候,原本黑色文字区域就会白光一闪,然后再出现文字,也就是我们说的闪烁了。而内存绘图的过程呢,是先创建一个内存DC,然后在这个DC上把要绘制的图形画好,之后一次性的填到屏幕上去。
示例代码如下:
HDC hDestDC;
//..此处得到目标的HDC和目标的RECT
HDC hdc = ::CreateCompatibleDC (hDestDC);
HBITMAP hBitmap = ::CreateCompatibleBitmap (hDestDC, rc.right, rc.bottom);
HBITMAP hOldBitmap = ::SelectObject (hDC, hBitmap);
//... 此处用hdc进行绘图
::BitBlt (m_hDestDC, rc.left, rc.top, rc.Width(), rc.Height(), hDC, rc.left, rc.top, SRCCOPY);
::SelectObject (hDC, hOldBitmap);当然,这样用起来不太方便,可以将这些操作封装到一个叫CMemDC的对象中,利用构造和析构自动进行这些操作。直接使用CMemDC还有一个好处,调试GDI时,如果图形都在内存中绘制,那么还是看不到绘图过程。
代码如果这样写:
GetWindowRect(&rc);
#ifdef _DEBUG
CMemDC dc(cdc.m_hDC, &rc);
#endif那么就既能享受内存绘图的好处又能方便调试了。
双缓冲绘图
转载地址:
GDI+使用双缓冲绘图
————————————————————————
我再来详细解释一下刚才实现双缓冲的具体步骤:
1、在内存中建立一块“虚拟画布”:
Bitmap bmp = new Bitmap(600, 600);
2、获取这块内存画布的Graphics引用:
Graphics g = Graphics.FromImage(bmp);
3、在这块内存画布上绘图:
g.FillEllipse(brush, i * 10, j * 10, 10, 10);
4、将内存画布画到窗口中
this.CreateGraphics().DrawImage(bmp, 0, 0);
====================
maybe better
dcMemory.CreateCompatibleDC(&dc);
bmp.CreateCompatibleBitmap(&dc,);
dcMemory.SelectObject(&bmp);
Graphics _Graphics(dcMemory.m_hDC);
_Graphics.DrawImage(_pImage,0,0,);
dc.BitBlt(0,0,,&dcMemory,0,0,SRCCOPY);
_Graphics.ReleaseHDC(dcMemory.m_hDC);
dcMemory.DeleteDC();
bmp.DeleteObject();
OnEraseBkgnd(CDC* pDC)需要返回TRUE。
实例解说双缓冲
转载地址:
昨天在论坛上,有人问起双缓冲的实现问题,想起网上这方面资料比较凌乱,而且多是 DirectX 相关的,今天特地在这里给大家简要的介绍一下双缓冲技术及其在 VC++ 的 GDI 绘图环境下的实现。
1、Windows 绘图原理
我们在 Windows 环境下看到各种元素,如菜单、按钮、窗口、图像,从根本上说,都是“画”出来的。这时的屏幕,就相当于一块黑板,而 Windows 下的各种 GDI 要素,如画笔、画刷等,就相当于彩色粉笔了。我们在黑板上手工画图时,是一笔一划的,电脑亦然。只不过电脑的速度比手工快的太多,所以在我们看起来好像所有的图形文字都是同时出现的。
2、普通绘图方式的局限
上述绘图方式我们暂且称之为普通绘图方式吧。虽然这种方式能满足相当一部分的绘图需要,但是当要绘制的对象太复杂,尤其是含有位图时,电脑便力不从心了。这时的画面会显示的很慢,对于运动的画面,会给人“卡”住了的感觉,总之一个字:不爽。
3、解决之道:双缓冲
双缓冲的原理可以这样形象的理解:把电脑屏幕看作一块黑板。首先我们在内存环境中建立一个“虚拟“的黑板,然后在这块黑板上绘制复杂的图形,等图形全部绘制完毕的时候,再一次性的把内存中绘制好的图形“拷贝”到另一块黑板(屏幕)上。采取这种方法可以提高绘图速度,极大的改善绘图效果。下面是原理图:
图一 双缓冲原理示意图
4、相关的函数介绍
1)、为屏幕 DC 创建兼容的内存 DC:CreateCompatibleDC()
if(!m_dcMemory.CreateCompatibleDC(NULL)) // CDC m_dcM
::PostQuitMessage(0);
} 2)、创建位图:CreateCompatibleBitmap()
m_Bmp.CreateCompatibleBitmap(&m_dcMemory, rt.Width(), rt.Height()); // CBitmap m_B3)、把位图选入设备环境:SelectObject(),可以理解为选择画布
::SelectObject(m_dcMemory.GetSafeHdc(), m_Bmp); 4)、把绘制好的图形“拷贝“到屏幕上:BitBlt()
pdcView-&BitBlt(0, 0, rt.Width(), rt.Height(), &m_dcMemory, 0, 0, SRCCOPY);
函数的具体用法详见 MSDN。有一句话我重复了多遍,再说一遍也无妨:MSDN是最好的老师。
5、本文给出了一个例子,用效果对比的方法说明普通绘图方式的局限和双缓冲技术的好处。
这个例子在一个 View 上画出很多半径渐变的圆,大家可以注意两种不同的绘图方式下动画的效果:
CreateCompatibleDC CreateCompatibleBitmap SelectObject&
物理HDC&设备底层会拥有显存等资源,但是兼容DC并没有给图像像素提供内存空间,因此兼容DC总是和BITMAP配合使用,这样一来,兼容DC就利用BITMAP的图像像素数据空间给自己提供类似于显存的内存空间.
这样有很多好处,以来我们可以在加载图片后,在图片上利用DC的各种绘图功能.请看如下示例:
兼容DC在建立之初,只有1*1像素的尺寸,SelectObject选择bitmap以后才可以进行绘图.
内存DC的可见区域是简单的区域,不像物理DC可见区域可能被其他窗口覆盖而产生复杂的可见区域.由于DC的任何绘图都需要考虑在可见区域内绘图,绝对不能超出可见区域的范围.因此每个GDI绘图输出最终都需要和构成复杂可见区域的每一个巨型区域进行剪裁输出,因此物理DC的绘图效果会比兼容DC速度慢一些.这也就是我们经常用兼容DC进行双缓存输出的一个原因
When a memory device context is created, it initially has a 1-by-1 monochrome bitmap selected into it. If this memory device context is used in CreateCompatibleBitmap, the bitmap that is created is a monochrome bitmap. To create a color bitmap, use the HDC
that was used to create the memory device context, as shown in the following code:
If an application sets the nWidth or nHeight parameters to zero, CreateCompatibleBitmap returns the handle to a 1-by-1 pixel, monochrome bitmap.
If a DIB section, which is a bitmap created by the CreateDIBSection function, is selected into the device context identified by the hdc parameter, CreateCompatibleBitmap creates a DIB section.
When you no longer need the bitmap, call the DeleteObject function to delete it.
在桌面画图
HDC hDC = ::GetDC(0);
HDC memDC = CreateCompatibleDC ( hDC );
int width = 200;
int height = 300;
HBITMAP memBM = CreateCompatibleBitmap ( hDC, width, height );
HBITMAP holdBM = (HBITMAP)SelectObject ( memDC, memBM );
RECT rect = {0,0,width,height};
HBRUSH newbrush=CreateSolidBrush(RGB(0,255,255));
HBRUSH oldbrush=(HBRUSH)SelectObject(memDC,&newbrush);
FillRect(memDC,&rect,newbrush);
SetTextColor(memDC,RGB(255,0,0));
SetBkColor(memDC,RGB(0,255,255));
::DrawText(memDC,& 是调用父类的OnEraseBkgnd函数,我们屏蔽此调用,只须直接return TRUE;即可。&,-1,&rect,DT_CENTER|DT_EDITCONTROL|DT_WORDBREAK|DT_VCENTER);
::BitBlt(hDC,0,0,width,height,memDC,0,0,SRCCOPY);
SelectObject(memDC,holdBM);
SelectObject(memDC,oldbrush);
DeleteObject(newbrush);
DeleteObject(memDC);
::ReleaseDC(NULL,hDC);转载地址:
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:230992次
积分:3965
积分:3965
排名:第2648名
原创:158篇
转载:35篇
评论:314条
(3)(4)(7)(5)(15)(7)(4)(6)(7)(2)(10)(17)(4)(1)(3)(3)(7)(11)(15)(14)(7)(8)(6)(1)(5)(7)(14)C#-gdi绘图,双缓冲绘图,Paint事件的触发---ShinePans - 推酷
C#-gdi绘图,双缓冲绘图,Paint事件的触发---ShinePans
在使用gdi技术绘图时,有时会发现图形线条不够流畅,或者在改变窗体大小时会闪烁不断的现象.(Use DoubleBuffer to solve it!)
& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & &
1.线条不流畅
:窗体在重绘时自身重绘与图形重绘之间存在时间差,导致二者图像显示不协调
2.改变窗体大小不流畅
:重绘时自身背景颜色与图形颜色频繁交替,造成视觉上的闪烁
& & & 下面,用四个图形例子解决这个问题 :
贝塞尔曲线
不规则图形
&思路:首先用
的构造方法创建一个位图实例,然后通过调用
Graphics类
FromImage方法
创建画布对象,最后调用Grapgics类的
DrawImage方法
来实现在窗体上绘制图形.
Bitmap(int width,int height);
//width 定义位图的宽度 &height 定义位图的高度
Bitmap lacalBitmap =new Bitmap(CilentRectangle.Width,CilentRectangle.Height);
&//创建一个与窗体工作区大小相同的位图实例
static Graphics FromImage(Image image);
// image:Image类的子类的实例引用
Bitmap localBitmap=new Bitmap(CilentRectangle.Width,CilentRectangle.Height)
&//创建位图实例
void DrawImage(Image image,int x,int y); &
& // image:要绘制的图像 &x:绘制的图像的左上角 x坐标 y:左上角y坐标
Graphics g=e.G//获取窗体画布
g.DrawImage(localBitmap,0,0); &//在窗体中绘制出内存中的图像
实现:由于Paint被 .net隐藏,我们需要在窗体代码中加上自己的Paint事件中绘制窗口
& & & & & & this.Paint += new System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
&private void InitializeComponent()
& & & & & & this.SuspendLayout();
& & & & & & //&
& & & & & & // Form1
& & & & & & //&
& & & & & & this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
& & & & & & this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.F
& & & & & & this.ClientSize = new System.Drawing.Size(388, 325);
& & & & & & this.MaximizeBox =
& & & & & & this.MinimizeBox =
& & & & & & this.Name = &Form1&;
& & & & & & this.Text = &双缓冲技术绘图&;
& & & & & & this.Paint += new System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
& & & & & & this.ResumeLayout(false);
this.Paint+=new System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & &
using System.Collections.G
using System.D
using System.D
using System.L
using System.T
using System.Windows.F
using System.Drawing.Drawing2D;
namespace DoubleBuffer
public partial class Form1 : Form
public Form1()
InitializeComponent();
private void Form1_Paint(object sender, PaintEventArgs e)
Bitmap localBitmap = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);
//创建位图实例
Graphics bitmapGraphics = Graphics.FromImage(localBitmap);
bitmapGraphics.Clear(BackColor);
bitmapGraphics.SmoothingMode = SmoothingMode.AntiA
PaintImage(bitmapGraphics);
Graphics g = e.G//获取窗体画布
g.DrawImage(localBitmap, 0, 0); //在窗体的画布中绘画出内存中的图像
bitmapGraphics.Dispose();
localBitmap.Dispose();
g.Dispose();
private void PaintImage(Graphics g)
GraphicsPath path = new GraphicsPath(new Point[]{ new Point(100,60),new Point(350,200),new Point(105,225),new Point(190,ClientRectangle.Bottom),
new Point(50,ClientRectangle.Bottom),new Point(50,180)}, new byte[]{
(byte)PathPointType.Start,
(byte)PathPointType.Bezier,
(byte)PathPointType.Bezier,
(byte)PathPointType.Bezier,
(byte)PathPointType.Line,
(byte)PathPointType.Line});
PathGradientBrush pgb = new PathGradientBrush(path);
pgb.SurroundColors = new Color[] { Color.Green, Color.Yellow, Color.Red, Color.Blue, Color.Orange, Color.LightBlue };
g.FillPath(pgb, path);
g.DrawString(&双缓冲绘图&, new Font(&宋体&, 18, FontStyle.Bold), new SolidBrush(Color.Red), new PointF(110, 20));
g.DrawBeziers(new Pen(new SolidBrush(Color.Green),2),new Point[] {new Point(120,100),new Point(120,120),new Point(120,100),new Point(120,150)});
g.DrawArc(new Pen(new SolidBrush(Color.Blue), 5), new Rectangle(new Point(120, 170), new Size(60, 60)), 0, 360);
g.DrawRectangle(new Pen(new SolidBrush(Color.Orange), 3), new Rectangle(new Point(240, 260), new Size(90, 50)));
Form1设计:
namespace DoubleBuffer
partial class Form1
/// &summary&
/// 必需的设计器变量。
/// &/summary&
ponentModel.IContainer components =
/// &summary&
/// 清理所有正在使用的资源。
/// &/summary&
/// &param name=&disposing&&如果应释放托管资源,为 true;否则为 false。&/param&
protected override void Dispose(bool disposing)
if (disposing && (components != null))
components.Dispose();
base.Dispose(disposing);
#region Windows 窗体设计器生成的代码
/// &summary&
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// &/summary&
private void InitializeComponent()
this.SuspendLayout();
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.F
this.ClientSize = new System.Drawing.Size(388, 325);
this.MaximizeBox =
this.MinimizeBox =
this.Name = &Form1&;
this.Text = &双缓冲技术绘图&;
this.Paint += new System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
this.ResumeLayout(false);
#endregion
Program.cs:
using System.Collections.G
using System.L
using System.Windows.F
namespace DoubleBuffer
static class Program
/// &summary&
/// 应用程序的主入口点。
/// &/summary&
[STAThread]
static void Main()
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
当变化窗体时,会导致图像出现变形,可把窗体属性中的ResizeReDrae 设置为 true
& & & & & & & & & &
& & & & & & & &&
& & & & & & &&
& & & & & & & & &
& & & & & & & & &&
& & & & &&
& & & & & & & & =
& & & & & &&
& & & & & & &&
& & & & & & & & & &&
& & & & & & & & &
已发表评论数()
&&登&&&陆&&
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见GDI+双缓冲帖PNG透明图片的时候背景问题
[问题点数:50分,结帖人lxw_it]
GDI+双缓冲帖PNG透明图片的时候背景问题
[问题点数:50分,结帖人lxw_it]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
相关帖子推荐:
匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。&&&您需要以后才能回答,未注册用户请先。}

我要回帖

更多关于 java双缓冲 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信