VC中的GDI双缓冲绘图


CBitmap* m_pOldBmp;
ATL程序中,可在atlgdi.h找到基于ATL程序的内存DC:
class CMemoryDC : public CDC { public: // Data members HDC m_hDCOriginal; RECT m_rcPaint; CBitmap m_bmp; HBITMAP m_hBmpOld; // Constructor/destructor CMemoryDC(HDC hDC, RECT& rcPaint) : m_hDCOriginal(hDC), m_hBmpOld(NULL) {


双缓冲绘图原理

普通绘图操作
物理DC绘图 操作 屏幕

双缓冲绘图操作
物理DC绘图 操作 屏幕
内存DC
实现过程

1. 2. 3. 4. 5. 6.
创建兼容DC 创建兼容位图 用兼容DC关联兼容位图 使用兼容DC绘图 将兼容DC拷贝到物理DC上 回收兼容位图和兼容DC,防止资源泄露
实现代码
CPaintDC dc(this);
// 绘图区域 CRect rectClient; GetClientRect(&rectClient);
// 创建兼容DC
CDC dcMem; dcMem.CreateCompatibleDC(&dc); // 创建兼容位图,使兼容DC与之关联,否则兼容DC则是一个1*1的单色位图 CBitmap bitmap;
SetViewportOrg(-m_rcPaint.left, -m_rcPaint.top);
} ~CMemoryDC() {
::BitBlt(m_hDCOriginal, m_rcPaint.left, m_rcPaint.top, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom m_rcPaint.top, m_hDC, m_rcPaint.left, m_rcPaint.top, SRCCOPY); SelectBitmap(m_hBmpOld);
bitmap.CreateCompatibleBitmap(&dc, rectClient.Width(), rectClient.Height());
CBitmap* pOldBitmap = dcMem.SelectObject(&bitmap); // 背景画刷 CBrush brush(RGB(255, 255, 255)); dcMem.FillRect(rectClient, &brush); // 使用dcMem绘图…… // DC拷贝 dc.BitBlt(0, 0, rectClient.Width(), rectClient.Height(), &dcMem, 0, 0, SRCCOPY); dcMem.SelectObject(pOldBitmap);
物理DC即可。如果图形非常复杂,窗口重绘又 比较频繁时可以使用内存DC,防止闪烁。
} };
使用已封装的内存DC
// 1.使用MFC下的CMemDC void CMyDialog::OnPaint() {
CPaintDC dcPaint(this);
CMemDC dcMem(dcPaint, this); CDC& dc = dcMem.GetDC(); // 使用dc刷背景,绘图…… } // 2.使用ATL下的CMemoryDC void CMyDialog::OnPaint() { CPaintDC dcPaint(this); CRect rectClient; GetClientRect(&rectClient); CMemoryDC dcMem((HDC)dcPaint, rectClient); // 使用dcMem刷背景,绘图…… }
VC中的GDI双缓冲绘图

用VC开发图形相关的程序时,通常是响应WM_PAINT消息,在窗口的 OnPaint()函数里使用CPaintDC类进行绘制操作。 当视图中的图形数据比较复杂时,OnPaint()被频繁调用会导致窗口的 闪烁。为了解决这个问题,可以使用双缓冲绘图方式,即使用内存DC 过渡。 由于物理DC的绘图区域是由其它窗口覆盖而产生的复杂区域,绘图输 出时需进行区域裁剪。而内存DC的绘图区域只是简单的矩形,因此用 内存DC效率更高。


MFC/ATL中封装的内存DC

安装了VS2008 SP1之后,afxcontrolbarutil.h里封装了可供MFC程序使用的内存DC,定义如下:
class CMemDC { public: AFX_IMPORT_DATA static BOOL m_bUseMemoryDC; CMemDC(CDC& dc, CWnd* pWnd); CMemDC(CDC& dc, const CRect& rect); virtual ~CMemDC(); CDC& GetDC() { return m_bMemDC ? m_dcMem : m_dc; } BOOL IsMemDC() const { return m_bMemDC; } BOOL IsVistaDC() const { return m_hBufferedPaint != NULL; } protected: CDC& BOOL HANDLE CDC CBitmap CRect }; m_dc; m_bMemDC; m_hBufferedPaint; m_dcMem; m_bmp; m_rect;
m_rcPaint = rcPaint; CreateCompatibleDC(m_hDCOriginal); ATLASSERT(m_hDC != NULL); m_bmp.CreateCompatibleBitmap(m_hDCOriginal, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom m_rcPaint.top); ATLASSERT(m_bmp.m_hBitmap != NULL); m_hBmpOld = SelectBitmap(m_bmp);
使用细节

创建兼容DC和兼容位图时,尽量使用封装类CDC,CBitmap。使用 GDI资源存在着资源释放的问题,如用GetDC得到的DC使用完需用 ReleaseDC释放;CreateDC或 CreateCompatibleDC最后需用DeleteDC 删除。而CDC、CBitmap、CBrush等对象在析构时会自动释放或删除, 从而保证资源不泄露。 新创建的兼容位图是空位图,底色是黑色,因此使用内存DC画图前要 用FillRect填充绘画区域,否则整个区域会成为黑色,或是用一幅位图 覆盖在内存DC上。 使用内存DC后,当前窗口频繁刷新时还是可以看到闪烁很严重,这是 因为每次窗口重绘时会擦除一次背景,屏蔽此操作即可。响应 WM_ERASEBKGND消息,在 OnEraseBkgnd函数中直接return TRUE, 即不调用窗口自带的擦除背景函数。
合集下载

cWinForm利用GDI的双缓冲技术来提高绘图效率

cWinForm利用GDI的双缓冲技术来提高绘图效率

C# WinForm利用GDI+的双缓冲技术来进步绘图效率前言进入.NET时代,Windows的绘图技术也从GDI晋级到了GDI+,从名字就能知道GDI+是对以前传统GDI 绘图技术的一次晋级,不过在微软几乎把所有的新技术都冠之.NET的情况下,GDI+竟然不叫做,还真让我感到有点意外了。

:〕GDI+在一种与设备无关的环境下提供了一套统一的绘图编程模型,极大的进步了Windows绘图编程的方便性,我们再也不用创立什么各种各样复杂的设备环境了,说实话,我如今想起来都头疼。

题归正传,关于如何进展GDI+的根本编程,我不能过多的加以描绘,假如有对此概念还不太清楚的朋友,建议先去理解一下相关的资料,我们在这里主要讨论的是一种进步绘图效率〔主要是动画效率〕的双缓冲技术在GDI+中的应用和实现。

实现目的为了能清楚的比照应用双缓冲技术前后的效果,我编写了一段程序来进展测试。

首先,我创立了一个普通的Windows Application,在主Form中,我放置了一个定时器:timer1,然后将它的Interval属性设置为10,然后在Form上放置两个按纽,分别用来控制定时器的开启和关闭,最后,我还放置了一个label控件,用来显示绘图的帧数。

测试程序在timer1的timer1_Tick事件中,我写下了如下的代码(其中flag是一个bool型标志变量):DateTime t1 = DateTime.Now;Graphics g = this.CreateGraphics();if(flag){brush = new LinearGradientBrush(new PointF(0.0f, 0.0f),new PointF(700.0f, 300.0f), Color.Red, Color.Blue);flag = false;}else{brush = new LinearGradientBrush(new PointF(0.0f, 0.0f),new PointF(700.0f, 300.0f), Color.Blue, Color.Red);flag = true;}for(int j = 0; j < 60; j ++){for(int i = 0; i < 60; i++){g.FillEllipse(brush, i * 10, j * 10, 10, 10);}}DateTime t2 = DateTime.Now;TimeSpan sp = t2 - t1;float per = 1000 / liseconds;运行后,我点击“开场〞按纽,效果如下列图所示:应用双缓冲以前的效果图〔帧数:5帧/秒〕正如大家所看到的,我在程序中使用循环画了几百个圆形,然后在每次的定时器脉冲事件中使用不同方向的线性渐变来对它们进展填充,形成了一个动画效果。

GDI+ 如何使用双缓冲绘制图像

GDI+ 如何使用双缓冲绘制图像

早前曾为此问题在CSDN发帖求助(GDI+ 如何使用双缓冲绘制图像),得到了一个GDI+下较可行的方法,虽然绘制效果比直接绘制要好一些,不过还不能跟GDI的双缓冲方式比肩。

现在,我终于找到了一个理想的实现方式,效果与GDI的实现不相上下,代码如下:/*C++ code*/RECT rc;GetClientRect(g_hwnd,&rc);Bitmap bmp(int(rc.right),int(rc.bottom));Graphics bmpGraphics(&bmp);bmpGraphics.SetSmoothingMode(SmoothingModeAntiAlias);/*Drawing on bitmap*/SolidBrush bkBrush(Color(0,0,0));bmpGraphics.FillRectangle(&bkBrush,0,0,rc.right,rc.bottom);/*Drawing on DC*/Graphics graphics(hdc);/*Important! Create a CacheBitmap object for quick drawing*/CachedBitmap cachedBmp(&bmp,&graphics);graphics.DrawCachedBitmap(&cachedBmp,0,0);以上的绘制代码最区别于网络上其他GDI+实现的一处就是,在最后添加了一个CacheBitmap对象用于快速绘制。

CacheBitmap是一个包含了bmp全部象素,并且针对graphics所关联的DC做过特别优化的位图对象。

这点可以从其构造参数上看到。

关于双缓冲的实现还有一点十分关键,虽然它不属于双缓冲实现的核心。

如果绘制需要经常的重绘背景,则需要自己拦截WM_ERASEBKGND消息,并在处理函数中什么也不做,即此消息发生时不重画背景,背景的重画在WM_PAINT中全权控制。

双缓冲绘图

双缓冲绘图

1创建画笔,画刷void CGraphView::OnDraw(CDC* pDC){CGraphDoc* pDoc = GetDocument( );ASSERT_V ALID(pDoc);// TODO: add draw code for native data here// 使用画笔第一步:创建一支新画笔CPen pen(PS_SOLID, 1, RGB(255,0,0));CPen pen(PS_DASHDOT, 1, RGB(255,0,0));//第二步:选择新画笔到设备环境中,同时保存旧画笔CPen * pOldPen = pDC->SelectObject(&pen);// 使用画刷第一步:创建一支新画刷CBrush brush(RGB(255,0,0)); //创建实体画刷CBrush brush(HS_CROSS, RGB(255,0,0)); //创建阴影模式画刷//第二步:选择新画刷到设备环境中,同时保存旧画刷CBrush * pOldBrush = pDC->SelectObject(&brush);// 常用的绘图函数////绘制一个彩色点pDC->TextOut(20, 20, "点");pDC->SetPixel(100, 40, RGB(255,0,0));//绘制直线pDC->TextOut(320, 20,"线段");pDC->MoveTo(400, 40);pDC->LineTo(500, 40);//绘制折线pDC->TextOut(20, 170, "折线");POINT polyline[4]={{240,240},{80,120},{240,120},{80,240}}; pDC->Polyline(polyline,4);//绘制矩形pDC->TextOut(320, 170, "矩形");pDC->Rectangle(390, 110, 600, 230);//绘制椭圆pDC->TextOut(20, 320, "椭圆");pDC->Ellipse(80, 260, 280, 380);//绘制多边形pDC->TextOut(320, 320, "多边形");POINT polygon[3]={{380,330},{530,260},{500,360}};pDC->Polygon(polygon,3);//第三步:恢复设备环境的旧画笔,以便释放新画笔// pDC->SelectObject(pOldPen);//第三步:恢复设备环境的旧画刷,以便释放新画刷// pDC->SelectObject(pOldBrush);}2创建字体的两种方法第一种创建字体的方法:使用CreateFont函数,如下定义:CFont::CreateFontBOOL CreateFont( int nHeight, //字体的高度int nWidth, //字体的宽度int nEscapement, //字体显示的角度int nOrientation, //字体的角度int nWeight, //字体的磅数BYTE bItalic, //斜体字体BYTE bUnderline, //带下划线的字体BYTE cStrikeOut, //带删除线的字体BYTE nCharSet, //所需的字符集BYTE nOutPrecision, //输出的精度BYTE nClipPrecision, //裁减的精度BYTE nQuality, //逻辑字体与输出设备的实际//字体之间的精度BYTE nPitchAndFamily, //字体间距和字体集LPCTSTR lpszFacename //字体名称);例子:font.CreateFont(12, // nHeight0, // nWidth0, // nEscapement0, // nOrientationFW_NORMAL, // nWeightFALSE, // bItalicFALSE, // bUnderline0, // cStrikeOutANSI_CHARSET, // nCharSetOUT_DEFAULT_PRECIS, // nOutPrecisionCLIP_DEFAULT_PRECIS, // nClipPrecision DEFAULT_QUALITY, // nQualityDEFAULT_PITCH | FF_SWISS, // nPitchAndFamily "Arial"); // lpszFacename一般只修改几项。

+MFC+GDI双缓冲避免图形闪烁

+MFC+GDI双缓冲避免图形闪烁

// TODO: Add your specialized creation code here SetTimer(1,10,NULL); return 0; } 利用定时器直接进行 10 毫秒的屏幕刷新,这样效果会出现不停的闪烁的情况. 解决方法利用双缓冲,首先触发 WM_ERASEBKGND,然后修改返回 TRUE; 定义变量: CBitmap *m_pBitmapOldBackground ; CBitmap m_bitmapBackground ; CDC m_dcBackground; //绘制背景 if(m_dcBackground.GetSafeHdc()== NULL|| (m_bitmapBackground.m_h Object == NULL)) {
MemDC.CreateCompatibleDC(NULL); //这时还不能绘图,因为没有地方画 ^_^ //下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的 大小 MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight); //将位图选入到内存显示设备中 //只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上 CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap); //先用背景色将位图清除干净,这里我用的是白色作为背景 //你也可以用自己应该用的颜色 MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255)); //绘图 MemDC.MoveTo(……); MemDC.LineTo(……);
} 说到这里可能又有人要说了 , 为什么一个简单图形看起来没有复杂图形那么闪呢? 这是因为复杂图形占的面积大,重画时造成的反差比较大,所以感觉上要闪得厉 害一些,但是闪烁频率要低。 那为什么动画的重画频率高,而看起来却不闪?这里,我就要再次强调了,闪烁 是什么?闪烁就是反差,反差越大,闪烁越厉害。因为动画的连续两个帧之间的 差异很小所以看起来不闪 。 如果不信 , 可以在动画的每一帧中间加一张纯白的帧 , 不闪才怪呢。 2、如何避免闪烁 在知道图形显示闪烁的原因之后,对症下药就好办了。首先当然是去掉 MFC 提 供的背景绘制过程了。实现的方法很多, * 可以在窗口形成时给窗口的注册类的背景刷付 NULL * 也可以在形成以后修改背景 static CBrush brush(RGB(255,0,0)); SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRU SH)brush); * 要简单也可以重载 OnEraseBkgnd(CDC* pDC)直接返回 TRUE. 这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,变 得一团乱。怎么办?这就要用到双缓存的方法了。双缓冲就是除了在屏幕上有图 形进行显示以外,在内存中也有图形在绘制。我们可以把要显示的图形先在内存 中绘制好 , 然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去 (这个过程非常快,因为是非常规整的内存拷贝)。这样在内存中绘图时,随便 用什么反差大的背景色进行清除都不会闪,因为看不见。当贴到屏幕上时,因为 内存中最终的图形与屏幕显示图形差别很小 (如果没有运动 , 当然就没有差别) , 这样看起来就不会闪。 3、如何实现双缓冲 首先给出实现的程序,然后再解释,同样是在 OnDraw(CDC *pDC)中: CDC MemDC; //首先定义一个显示设备对象 CBitmap MemBitmap;//定义一个位图对象 //随后建立与屏幕显示兼容的内存显示设备

C#环境下GDI+绘图效率研究

C#环境下GDI+绘图效率研究

C#环境下GDI+绘图效率研究作者:温铭毅来源:《中国科技博览》2015年第05期[摘要]本文在简单介绍GDI和GDI+的基础上,指出GDI+在绘制动画时效率不足的劣势,并对三种不同的GDI+动画绘图方式进行了详细的阐述;最后实现了一个GDI+界面绘图程序,比较和验证了三种不同方式的绘图性能。

实验表明,使用双缓冲和bitblt结合的方式,可以有效的解决直接绘制方式的闪烁问题,提高绘图效率。

[关键词]GDI+;动态绘图;双缓冲;bitblt中图分类号:P631.84 文献标识码:A 文章编号:1009-914X(2015)05-0134-011.引言GDI是微软公司的著名的二维图形引擎,GDI+则是微软公司为了提高显示效果而推出的一种新型图形引擎。

GDI是Graphics Device Interface的缩写,含义是图形设备接口,它的主要任务是负责系统与绘图程序之间的信息交换,处理所有Windows程序的图形输出。

GDI+是Windows XP中的一个子系统,它主要负责在显示屏幕和打印设备输出有关信息,它是一组通过C++类实现的应用程序编程接口。

顾名思义,GDI+是以前版本GDI的继承者,出于兼容性考虑,Windows XP仍然支持以前版本的GDI,但是在开发新应用程序的时候,开发人员为了满足图形输出需要应该使用GDI+,因为GDI+对以前的Windows版本中GDI进行了优化,并添加了许多新的功能。

作为图形设备接口的GDI+使得应用程序开发人员在输出屏幕和打印机信息的时候无需考虑具体显示设备的细节,他们只需调用GDI+库输出的类的一些方法即可完成图形操作,真正的绘图工作由这些方法交给特定的设备驱动程序来完成,GDI+使得图形硬件和应用程序相互隔离.从而使开发人员编写设备无关的应用程序变得非常容易。

Microsoft Windows GDI+服务分为以下3个主要部分:(1)二维矢量图形。

矢量图形由图元组成,而图元则由一系列坐标系统的点集组成。

GDI实现双缓冲

GDI实现双缓冲
/* 保存窗口句柄与程序实例句柄到全局变量 */
main_window_handle = hwnd;
hinstance_app = hinstance;
/* global_hdc为当前窗口的hdc buffer_hdc为后备缓冲 */
/* 初始化 */
global_hdc = GetDC(main_window_handle);
在上篇文章中将我要用C语言重新写一个俄罗斯方块,使用的是GDI的绘图模式(目前正在移植到DX上去,想添加一些更好友好的动画)。数据与动画分离,动画的帧率保持在30左右。但是绘图的时候画面出现了强烈的闪动。
排查了一下原因,不是由于电脑性能的原因(如果画几个线就闪的话那就糟了),而是由于采用的动画方式导致的。我采用的是擦除,绘制,擦除,绘制的模式。没有使用双缓冲。要解决画面闪烁,就要使用到双缓冲。
if(global_hdc)
ReleaseDC(main_window_handle,global_hdc);
if(buffer_hdc)
DeleteDC(buffer_hdc);
if(bmp)
DeleteObject(bmp);
/* 程序返回 */
return messages.wParam;
#define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
/* 窗口过程 */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
void Clear_Window(HDC hdc);
HWND main_window_handle = NULL; //保存窗口句柄的全局变量

c# GDI+简单绘图

c# GDI+简单绘图最近对GDI+这个东西接触的比较多,也做了些简单的实例,比如绘图板,仿QQ截图等.最早接触这个类,是因为想做仿QQ截图的效果.巧的很,学会了如何做截图后,.NET课堂上老师也正巧要讲关于c#绘图方面的知识,并且我自己又在网上学习金老师的培训班,也是要用到这个类.在学习中有一些体会,所以准备把这些体会记下来,因为内容比较多,可能我会分几次写.废话不多说了,我们先来认识一下这个GDI+,看看它到底长什么样.GDI+:Graphics Device Interface Plus也就是图形设备接口,提供了各种丰富的图形图像处理功能;在C#.NET中,使用GDI+处理二维(2D)的图形和图像,使用DirectX处理三维(3D)的图形图像,图形图像处理用到的主要命名空间是System . Drawing:提供了对GDI+基本图形功能的访问,主要有Graphics 类、Bitmap类、从Brush类继承的类、Font类、Icon类、Image类、Pen类、Color类等.大概了解了什么是GDI+后,我们来看一下绘图要用到的主要工具,要画图,肯定要画板吧,在C#中画板可以通过Graphics这个类来创建,有了画板,总得弄个笔什么之类的吧,不然怎么画呀,难不成我们用手指画.笔又可以分好多种类,比如铅笔,画刷等.它们的区别主要是铅笔可以用来画线条,而画刷呢,嘿嘿,自己考虑下.在c#中我们可以用Pen,Brush类来实现类似功能.颜料则自然是用Color类了.有了工具,我们就可以开始动手了!(所需命名空间:using ;)实现效果:在空白窗体中画基本图形首先准备一个画板:创建一个画板主要有3种方式:A: 在窗体或控件的Paint事件中直接引用Graphics对象B: 利用窗体或某个控件的CreateGraphics方法C: 从继承自图像的任何对象创建Graphics对象这次我们就先以A为例说明问题:private void Form1_Paint(object sender, PaintEventArgs e){Graphics g = ; }然后,我们要只笔:private void Form1_Paint(object sender, PaintEventArgs e){Graphics g = ; Pen p = new Pen, 2);private void Form1_Paint(object sender, PaintEventArgs e){Graphics g = ; Pen p = new Pen, 2);1.首先我们来看下上一片中我们使用过的Pen.Pen的属性主要有: Color(颜色),DashCap(短划线终点形状),DashStyle(虚线样式),EndCap(线尾形状), StartCap(线头形状),Width(粗细)等.我们可以用Pen 来画虚线,带箭头的直线等Pen p = new Pen, 5);下来我们来看下Brush的使用作用:我们可以用画刷填充各种图形形状,如矩形、椭圆、扇形、多边形和封闭路径等,主要有几种不同类型的画刷:•SolidBrush:画刷最简单的形式,用纯色进行绘制•HatchBrush:类似于 SolidBrush,但是可以利用该类从大量预设的图案中选择绘制时要使用的图案,而不是纯色•TextureBrush:使用纹理(如图像)进行绘制•LinearGradientBrush:使用沿渐变混合的两种颜色进行绘制•PathGradientBrush :基于编程者定义的唯一路径,使用复杂的混合色渐变进行绘制我们这里只是简单介绍使用其中的几种:Graphics g = ();Rectangle rect = new Rectangle(10, 10, 50, 50);标轴变换在winform中的坐标轴和我们平时接触的平面直角坐标轴不同,winform中的坐标轴方向完全相反:窗体的左上角为原点(0,0),水平向左则X增大,垂直下向则Y增大接下来,我们来实际操作下,通过旋转坐标轴的方向来画出不同角度的图案,或通过更改坐标原点的位置来平衡坐标轴的位置.Graphics g = ();后我们来看下Graphics这个画板上我们还可以画什么其实我们上面用到的都是在画一些简单的图形,直线,矩形,扇形,圆孤等,我们还可以用它来绘制图片,这可以用它的DrawImage方法.这里我不详细讲解,大家有兴趣可以自己去MSDN了解下.我们后面会讲到的截图就会用到这个方法.感谢大家的支持,这几天从早忙到晚,一个字累呀!!!现在挺困的,但是又不习惯这么早睡觉,哎~~还是利用这个时间继续来写第三篇吧.前两篇已经基本向大家介绍了绘图的基本知识.那么,我就用我们上两篇所学的,做几个例子.我们先来做一个简单的----仿QQ截图,关于这个的例子其实网上已经有这方面的资料了,但是为了文章的完整性,还是觉得有必要讲解.我们先来看一下效果:(图1)(图2)接下来看看这是如何做到的.思路:聊天窗体上有一个截图按钮,点击按钮后,程序将整个屏幕画在一个新的全屏窗体上,然后显示这个窗体.因为是全屏的窗体,并且隐藏了菜单栏、工具栏等,所以在我们看来就好像是一个桌面的截图,然后在这个新窗体上画矩形,最后保存矩形中的内容并显示在原来的聊天窗体中.步骤:A.新建一个窗体.命名为Catch.然后设置这个窗体的FormBorderStyle为None,WindowState为Maximized.B.我们对代码进行编辑:using System;usingusing;using;using;using;usingnamespace Client{public partial class Catch : Form{public Catch(){InitializeComponent();}#region用户变量private Point DownPoint = ;建了Catch窗体后,我们在截图按钮(位于聊天窗体上)上加入以下事件:private void bCatch_Click(object sender, EventArgs e){if{(); [0].; [0].);是在绘制第二个图形时,我们先用与底色相同的颜色将上次绘制的图形重新绘制一下.但这往往需要底色为纯色时使用.2.我们并不直接将图形画在画板上,我们用一个图片A来保存原画板上的图片.然后再新建一个与图片A相同的图片B,将我们要绘制的图形画在该图片B上,然后再将该图片B 画在画板上.这样图片A并没有被改变.于是第二次画的时候我们还是同样新建一个与图片A相同的图片进行绘制.那么上一次的图形就不会被保留下来.问题也就解决了.前几篇我已经向大家介绍了如何使用GDI+来绘图,并做了一个截图的实例,这篇我向大家介绍下如何来做一个类似windows画图的工具.个人认为如果想做一个功能强大的绘图工具,那么单纯掌握GDI还远远不够,我的目前也只能做一个比较简单的绘图工具了.不足之处,欢迎大家讨论!先来看一下最终效果吧:主要实现功能:画直线,矩形,橡皮,圆形,切换颜色,打开图片,保存图片,清除图片,手动调节画布大小;软件刚启动时,为一张空白画布,我们可以直接在画布上绘画,也可以通过菜单中的“打开”,导入一张图片,然后我们就可以在这张图片上进行绘制。

gdi双缓冲绘图

gdi双缓冲绘图.txt30生命的美丽,永远展现在她的进取之中;就像大树的美丽,是展现在它负势向上高耸入云的蓬勃生机中;像雄鹰的美丽,是展现在它搏风击雨如苍天之魂的翱翔中;像江河的美丽,是展现在它波涛汹涌一泻千里的奔流中。

以下是最简单的双缓冲代码,注释的地方很重要:CRect rect;static BOOL bColor=false;GetClientRect(&rect);CDC dcMem;dcMem.CreateCompatibleDC(pDC); //创建与视图的设备相兼容的内存设备,新的设备不具有与原设备相同的设备属性与背景色.CBitmap bmp;bmp.CreateCompatibleBitmap(pDC,rect.right,rect.bottom); //创建一个与视图兼容的位图,只有根据原设备来创建位图,才能从设备中获取像素点组成位图,因为双缓冲需要保留原设备中已有的图像,因此需要调用这个方法CBitmap* pOldBmp=dcMem.SelectObject(&bmp); //选择位图,只是修改了设备属性,并没有真正绘图.if(bColor){dcMem.SelectStockObject(WHITE_PEN);}else{dcMem.SelectStockObject(BLACK_PEN);}bColor=!bColor;for(int i=0;i<rect.Width();i++) {dcMem.MoveTo(i,0);dcMem.LineTo(i,rect.bottom);}pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0,SRCCOPY); //将在内存中绘制好的图像重新显示到视图中,pDC与dcMem不必兼容.dcMem.SelectObject(pOldBmp);pOldBmp->DeleteObject();双缓冲技术绘图当数据量很大时,绘图可能需要几秒钟甚至更长的时间,而且有时还会出现闪烁现象,为了解决这些问题,可采用双缓冲技术来绘图。

使用GDI在窗口上画出一个图片

dc = getdc(窗口句柄)
memhdc = createcompitabledc(dc)
selectobject(memhdc,图片句柄)
BitBlt(hDc,0,0,100,24,memdc,0,0,SRCCOPY)
===============================
Dim img As Long, img_W As Long, img_H As Long
GdipLoadImageFromFile StrPtr("C:\TestImage.png"), img
GdipGetImageWidth img, img_W
GdipGetImageHeight img, img_H
注意咯 第三~第六个参数是原来图片中要截取的部分;第七~第十呢则是画到哪里以及画出来多大的设置。
(4)贴图刷
贴图刷子 跟其它刷子一样 我们需要创建刷子 另外我们需要先初始化图片:)
来看代码:
Dim img As Long, textureBrush As Long
GdipLoadImageFromFile StrPtr("C:\TestImage.png"), img
'GdipDisposeImage img
当然 这里面载入长宽是可选的。
最后呢 别忘记释放Image,否则内存突然没了可不要找我~
(2)来自资源文件:GdipLoadImageFromStream
这个函数主要是用来从资源文件(RES)载入图像的,怎么载入呢?我们来看函数,函数是从Stream载入,但是我们VB6没有集成Stream对象,从RES读取出来(LoadResData)也只是返回Byte()。不过很好,OLE提供了一个函数能够将Byte()变为一个IStream对象——我们需要这个API

C#使用BufferedGraphics实现GDI+双缓冲绘图

使用双缓冲的图形可以减少或消除重绘显示图面时产生的闪烁。

使用双缓冲时,更新的图形首先被绘制到内存的缓冲区中,然后,此缓冲区的内容被迅速写入某些或所有显示的图面中。

显示图形的重写相对简短,这通常可以减少或消除有时在更新图形时出现的闪烁。

使用C# GDI+绘图,实现双缓冲绘图有几种方法,在这篇文章中,将介绍其中的一种——使用BufferedGraphics实现GDI+双缓冲绘图。

下面的代码示例演示如何使用BufferedGraphics对象绘制以下图形,这些图形使用几种类型的缓冲实现。

单击窗体将启动或停止一个计时器,该计时器将引起绘制更新。

绘制更新使您可以观察双缓冲的效果。

右击窗体将循环使用下列绘制模式:∙对于Form,直接绘制到Handle。

∙对使用OptimizedDoubleBuffer控件样式的OnPaint方法进行重写,以进行绘制∙对于不使用OptimizedDoubleBuffer控件样式的窗体方法,重写OnPaint方法以进行绘制。

在每种模式下都将绘制文本,以标识当前模式并描述按下每个鼠标按钮时发生的行为。

using System;using ponentModel;using System.Drawing;using System.Windows.Forms;namespace BufferingExample{public class BufferingExample : Form{private BufferedGraphicsContext context;private BufferedGraphics grafx;private byte bufferingMode;private string[] bufferingModeStrings ={ "Draw to Form without OptimizedDoubleBufferring control style","Draw to Form using OptimizedDoubleBuffering control style","Draw to HDC for form" };private System.Windows.Forms.Timer timer1;private byte count;public BufferingExample() : base(){// Configure the Form for this example.this.Text = "User double buffering";this.MouseDown += newMouseEventHandler(this.MouseDownHandler);this.Resize += new EventHandler(this.OnResize);this.SetStyle( ControlStyles.AllPaintingInWmPaint | erPaint, true );// Configure a timer to draw graphics updates.timer1 = new System.Windows.Forms.Timer();timer1.Interval = 200;timer1.Tick += new EventHandler(this.OnTimer);bufferingMode = 2;count = 0;// Retrieves the BufferedGraphicsContext for the// current application domain.context = BufferedGraphicsManager.Current;// Sets the maximum size for the primary graphics buffer// of the buffered graphics context for the application// domain. Any allocation requests for a buffer larger// than this will create a temporary buffered graphics// context to host the graphics buffer.context.MaximumBuffer = new Size(this.Width+1,this.Height+1);// Allocates a graphics buffer the size of this form// using the pixel format of the Graphics created by// the Form.CreateGraphics() method, which returns a// Graphics object that matches the pixel format of the form.grafx = context.Allocate(this.CreateGraphics(),new Rectangle( 0, 0, this.Width, this.Height ));// Draw the first frame to the buffer.DrawToBuffer(grafx.Graphics);}private void MouseDownHandler(object sender, MouseEventArgs e){if( e.Button == MouseButtons.Right ){// Cycle the buffering mode.if( ++bufferingMode > 2 )bufferingMode = 0;// If the previous buffering mode used// the OptimizedDoubleBuffering ControlStyle,// disable the control style.if( bufferingMode == 1 )this.SetStyle( ControlStyles.OptimizedDoubleBuffer, true );// If the current buffering mode uses// the OptimizedDoubleBuffering ControlStyle,// enabke the control style.if( bufferingMode == 2 )this.SetStyle( ControlStyles.OptimizedDoubleBuffer, false );// Cause the background to be cleared and redraw.count = 6;DrawToBuffer(grafx.Graphics);this.Refresh();}else{// Toggle whether the redraw timer is active.if( timer1.Enabled )timer1.Stop();elsetimer1.Start();}}private void OnTimer(object sender, EventArgs e){// Draw randomly positioned ellipses to the buffer.DrawToBuffer(grafx.Graphics);// If in bufferingMode 2, draw to the form's HDC.if( bufferingMode == 2 )// Render the graphics buffer to the form's HDC.grafx.Render(Graphics.FromHwnd(this.Handle));// If in bufferingMode 0 or 1, draw in the paint method.elsethis.Refresh();}private void OnResize(object sender, EventArgs e){// Re-create the graphics buffer for a new window size.context.MaximumBuffer = new Size(this.Width+1,this.Height+1);if( grafx != null ){grafx.Dispose();grafx = null;}grafx = context.Allocate(this.CreateGraphics(),new Rectangle( 0, 0, this.Width, this.Height ));// Cause the background to be cleared and redraw.count = 6;DrawToBuffer(grafx.Graphics);this.Refresh();}private void DrawToBuffer(Graphics g){// Clear the graphics buffer every five updates.if( ++count > 5 ){count = 0;grafx.Graphics.FillRectangle(Brushes.Black, 0, 0, this.Width, this.Height);}// Draw randomly positioned and colored ellipses.Random rnd = new Random();for( int i=0; i<20; i++ ){int px = rnd.Next(20,this.Width-40);int py = rnd.Next(20,this.Height-40);g.DrawEllipse(new Pen(Color.FromArgb(rnd.Next(0, 255),rnd.Next(0,255), rnd.Next(0,255)), 1),px, py, px+rnd.Next(0, this.Width-px-20), py+rnd.Next(0, this.Height-py-20));}// Draw information strings.g.DrawString("Buffering Mode:"+bufferingModeStrings[bufferingMode], new Font("Arial", 8), Brushes.White, 10, 10);g.DrawString("Right-click to cycle buffering mode", new Font("Arial", 8), Brushes.White, 10, 22);g.DrawString("Left-click to toggle timed display refresh", new Font("Arial", 8), Brushes.White, 10, 34);}protected override void OnPaint(PaintEventArgs e){grafx.Render(e.Graphics);}[STAThread]public static void Main(string[] args){Application.Run(new BufferingExample());}}}。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
相关文档
最新文档