Win32 GDI 非矩形区域剪裁,双缓冲技术

传统的Win32通过GDI提供图形显示的功能,包括了基本的绘图功能,如画线、方块、椭圆等等,高级功能包括了多边形和Bezier的绘制。这样app就不用关心那些图形学的细节了,有点类似于UNIX上的X-window协议。你信或者不信,那些看上去很花哨的控件,其实就是一笔一划画上去的而已。GDI提供了画笔(用于线条)、画刷(用于填充)、调色板(用于支持256色显示)、字体(用于文字)。如果简单的图形不足以表达,你可以使用位图和画布(DC,设备上下文)直接将图像绘制到屏幕上去。此外,GDI还支持一些简单的坐标变换,以方便app缩放、平移绘制的内容,特别是支持打印机操作。在打印设备上,程序使用英寸而不是像素来绘画。不得不说,GDI的变换和位图部分不是很好用,DIB和DDB简直是折磨人,以至于经典的《Windows程序设计》大部分篇幅都在教你如何写字和画画。

GDI+内部调用了GDI,提供了一些更高级的新功能,如半透明(Alpha channel)、路径合并(Path Combination)、抗锯齿(Anti-alias)和渐变色(Gradient)。另外还包括了比GDI更全面的颜色/坐标变换方法,以及方便的图像显示。除了支持BMP以外,添加了GIF、PNG、JPG等编码解码器,方便app导入导出图像文件。从Windows Vista开始,图形显示领域有弱化、代替GDI的趋势。XP的普通窗口运行于GDI窗口管理器中,而从Vista开始所有窗口都运行于D3D的窗口管理器中。也就是说以前你往显示器上点颜色,而你现在只不过在往一个3D游戏中帖平面图而已。新的WPF程序的图形呈现完全依赖于D3D而跳过了GDI、GDI+。Win 7新出现的Direct 2D就致力于用显卡的加速功能代替GDI进行二维图形和文字的显示。

不知道Win8的Metro App是不是也是基于D3D的呢~~~

充分利用硬件加速好像是一个趋势

例子1

====

如何画出如“唐老鸭”这样一个造型的窗口。

窗口风格为 WS_POPUP,所以创建的窗口没有标题栏。但是当窗口没有标题栏后,我们就无法用拖动标题栏的办法来移动窗口,如果让窗口一动不动呆在屏幕中间显然是不行的,这里有一个替代办法,我们可以响应按下鼠标左键的消息,在 WM_LBUTTONDOWN 消息中想窗口发送 WM_NCLBUTTONDOWN (非客户区鼠标按下消息) 位置在 HTCAPTION 来模拟鼠标按在标题栏中来实现移动的功能。

Windows 里有专门的 API 来实现特殊形状的窗口,步骤是首先建立区域(Region),Region 可以合并,这样一来就可以用几个简单的区域合并出一个复杂的区域,建立、合并区域和设置窗口的 API 主要有以下几条:

CreateRectRgn(Left,Top,Right,Bottom) - 建立矩型区域

CreateEllipticRgn(Left,Top,Right,Bottom) - 建立椭圆区域

CreatePolygonRgn(lpPoints,NumberOfPoints,Mode) - 建立多边形区域,这些API返回区域句柄

CombineRgn(hDest,hSource1,hSource2,CombineMode) - 合并区域

SetWindowRgn(hWnd,hRgn,bRedraw) - 根据区域设置窗口形状

本程序的方法是扫描位图的点,按行设置区域,然后合并到总的区域中。

例子2

====

双缓冲技术

用VC做的画图程序,当所画的图形大于屏幕时,在拖动滚动条时屏幕就会出现严重的闪烁,为了解决这一问题,就得使用双缓冲来解决。程序产生严重的闪烁问题是因为画图过程中前后两次的画面反差很大造成的人的视觉的闪烁。因为在VC中每次在调用OnDraw时系统都是先用背景画刷将画布清除再执行画图命令,这样在你每次移动滚动条时每执行一次OnDraw就会有一个空白页,这样和你的最终结果图象之间有一个很大的反差,因而看起来闪烁,而且滚动条滚动越快闪烁越严重。当然,你可以将背景画刷设为NULL,这样可以解决闪烁问题,但是不能将先前的图象擦除,这样整个屏幕就显得很乱。

设备描述符抽象了不同的硬件环境为标准环境,用户编写时使用的是这个虚拟的标准环境,而不是真实的硬件,与真实硬件打交道的工作一般交给系统和驱动程序去完成(这同样解释了为什么我们需要经常更新驱动程序的问题)。使用在windows图形系统(gdi,而不包括direct x)上面,就体现在一系列的图形DC上面,我们如果要在gdi上面绘图,就必须先得到图形DC的句柄(handle),然后在指定句柄的基础上进行图形操作。

但是WM_PAINT消息响应的频度太高了,比如最小化最大化,移动窗体,覆盖等等都引起重绘,经常的这样画图,很是消耗性能;在有些场合,比如随机作图的场合,每一次就改变,还导致了程序的无法实现。怎么解决后一种问题呢。

ms在msdn的例子里面交给我们document/view的经典解决办法,将图形的数据存储在document类里面,view类只是根据这些数据绘图。比如你要画个圆,只是将圆心和半径存在document里面,view类根据这个里面的数据在屏幕上面重新绘制。那么,我们只需要随机产生一次数据就可以了。

这样还是存在性能的问题,于是我们开始考虑另外的解决方法。我们知道,将内存中的图片原样输出到屏幕是很快的,这也是我们在dos时代经常做的事情,能不能在windows也重新利用呢?答案就是内存缓冲绘图。

CRect rc; // 定义一个矩形区域变量

GetClientRect(rc);

int nWidth = rc.Width();

int nHeight = rc.Height();

CDC *pDC = GetDC(); // 定义设备上下文

CDC MemDC; // 定义一个内存显示设备对象

CBitmap MemBitmap; // 定义一个位图对象

//建立与屏幕显示兼容的内存显示设备

MemDC.CreateCompatibleDC(pDC);

//建立一个与屏幕显示兼容的位图,位图的大小可选用窗口客户区的大小

MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);

//将位图选入到内存显示设备中,只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上

CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap);

//先用背景色将位图清除干净,否则是黑色。这里用的是白色作为背景

MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));

//绘图操作等在这里实现

MemDC.MoveTo(……);

MemDC.LineTo(……);

MemDC.Ellipse(……);

//将内存中的图拷贝到屏幕上进行显示

pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);

//绘图完成后的清理

MemDC.SelectObject(pOldbitmap);

MemBitmap.DeleteObject();

在缓冲区还可以实现很多高级的图形操作,比如透明,合成等等,取决于具体的算法,需要对内存直接操作(其实就是当年dos怎么做,现在还怎么做)。

为什么不能用全局变量保存DC?

DC需要占用一定的内存,那么在频繁的页面调度中,位置难免改变,于是用来标志指针的句柄也就不同了。

为什么动画的重画频率高,而看起来却不闪?

闪烁是什么?闪烁就是反差,反差越大,闪烁越厉害。因为动画的连续两个帧之间的差异很小所以看起来不闪。如果不信,可以在动画的每一帧中间加一张纯白的帧,不闪才怪呢。

电力系统的网络图形的CAD软件,在一个窗口中往往要显示成千上万个电力元件,而每个元件又是由点、线、圆等基本图形构成。如果真要在一次重绘过程重画这么多元件,可想而知这个过程是非常漫长的。如果加上了图形的浏览功能,鼠标拖动图形滚动时需要进行大量的重绘,速度会慢得让用户将无法忍受。怎么办?只有再研究研究MFC的绘图过程了。

实际上,在OnDraw(CDC *pDC)中绘制的图并不是所有都显示了的,例如:你在OnDraw中画了两个矩形,在一次重绘中虽然两个矩形的绘制函数都有执行,但是很有可能只有一个显示了,这是因为MFC本身为了提高重绘的效率设置了裁剪区。裁剪区的作用就是:只有在这个区内的绘图过程才会真正有效,在区外的是无效的,即使在区外执行了绘图函数也是不会显示的。因为多数情况下窗口重绘的产生大多是因为窗口部分被遮挡或者窗口有滚动发生,改变的区域并不是整个图形而只有一小部分,这一部分需要改变的就是pDC中的裁剪区了。因为显示(往内存或者显存都叫显示)比绘图过程的计算要费时得多,有了裁剪区后显示的就只是应该显示的部分,大大提高了显示效率。但是这个裁剪区是MFC设置的,它已经为我们提高了显示效率,在进行复杂图形的绘制时如何进一步提高效率呢?那就只有去掉在裁剪区外的绘图过程了。可以先用pDC->GetClipBox()得到裁剪区,然后在绘图时判断你的图形是否在这个区内,如果在就画,不在就不画。但如果你的绘图过程不复杂,这样做可能对你的绘图效率不会有提高。

例子3

====

在窗口中显示出按正弦曲线起伏排列的“龙腾虎跃”五个楷体大字。窗口背景为灰色,文字前景则为一幅256色位图,就好象是把彩图剪成文字粘贴在窗口上一样。

调用BeginPath()函数开始路径定义

定义路径的GDI绘图函数包括:

AngleArc Arc ArcTo Chord *CloseFigure

Ellipse *ExtTextOut *LineTo *MoveToEx Pie

*PolyBezier *PolyBezierTo PolyDraw *Polygon *Polyline

*PolyLineTo *PolyPolygon *PolyPolylin Rectangl RoundRect

*TextOut

调用EndPath()函数结束路径定义。

绘制路径轮廓StrokePath(),填充路径FillPath(),绘制轮廓并填充StrokeAndFillPath(),把路径转换成区域PathToRegion(),把路径直线化FlattenPath(),提取路径数据GetPath(),加宽路径WidenPath()和设置裁剪路径SelectClipPath()等。

m_fontKaiTi.CreateFont(200 , 0 , 0 , 0 , FW_BLACK ,

FALSE , FALSE , FALSE ,

GB2312_CHARSET ,

OUT_DEFAULT_PRECIS ,

CLIP_DEFAULT_PRECIS ,

DEFAULT_QUALITY ,

FIXED_PITCH | FF_MODERN,

"楷体_GB2312");

RECT rect;

GetClientRect(&rect);

CFont* pOldFont=(CFont*)pDC->SelectObject(&m_fontKaiTi);

pDC->SetBkMode(TRANSPARENT);

//定义路径

pDC->BeginPath();{

pDC->TextOut(0,10,"龙",2);

pDC->TextOut(200,10,"腾",2);

pDC->TextOut(400,10,"虎",2);

pDC->TextOut(600,10,"跃",2); }

pDC->EndPath();

pDC->SelectObject(pOldFont);

//检取路径数据

int nCount=pDC->GetPath(NULL,NULL,0);

CPoint* points=new CPoint[nCount];

BYTE* bytes=new BYTE[nCount];

pDC->GetPath(points,bytes,nCount);

//对路径定义点按正弦曲线进行变换

int i;

for(i=0;i< nCount;i++)>/p>

points[i].y=points[i].y+(int)(80*sin(points[i].x

/300.*3.1415926)+100);

//重建一个新的路径

CPoint ptStart;

pDC->BeginPath();{

for(i=0;i< nCount;i++){>/p>

switch(bytes[i]){

//移动当前点位置

case PT_MOVETO:

pDC->MoveTo(points[i]);

ptStart=points[i];

break;

//画直线

case PT_LINETO:

pDC->LineTo(points[i]);

break;

//画贝塞尔曲线

case PT_BEZIERTO:

pDC->PolyBezierTo(points+i,3);

i=i+2;

break;

//画贝塞尔曲线并封闭图形

case PT_BEZIERTO|PT_CLOSEFIGURE:

points[i+2]=ptStart;

pDC->PolyBezierTo(points+i,3);

i=i+2;

break;

//画直线并封闭图形

case PT_LINETO|PT_CLOSEFIGURE:

pDC->LineTo(ptStart);

break;

}

pDC->EndPath();

//绘制窗口灰色背景

CBrush* pOldBrush=(CBrush*)(pDC->SelectStockObject(GRAY_BRUSH));

pDC->Rectangle(&rect);

pDC->SelectObject(pOldBrush);

//设置裁剪路径

pDC->SetPolyFillMode(WINDING);

pDC->SelectClipPath(RGN_COPY);

//用位图填充裁剪区域

CBitmap bmp;

CBitmap* pBmpOld;

bmp.LoadBitmap(IDB_BMP);

CDC dcMem;

dcMem.CreateCompatibleDC(pDC);

pBmpOld=dcMem.SelectObject(&bmp);

pDC->StretchBlt(0,0,rect.right,rect.bottom,

&dcMem,0,0,600,100,SRCCOPY);

dcMem.SelectObject(pBmpOld);

dcMem.DeleteDC();

bmp.DeleteObject();

Win32 GDI 非矩形区域剪裁,双缓冲技术,布布扣,bubuko.com

时间: 2024-10-12 17:43:09

Win32 GDI 非矩形区域剪裁,双缓冲技术的相关文章

OpenGL中实现双缓冲技术

在OpenGL中实现双缓冲技术的一种简单方法: 1.在调用glutInitDisplayMode函数时, 开启GLUT_DOUBLE,即glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);.这里将我们惯用的GLUT_SINGLE替换为GLUT_DOUBLE,意为要使用双缓冲而非单缓冲. 2. 调用glutDisplayFunc(display)注册回调函数时, 在回调函数中所有绘制操作完成后调用glutSwapBuffers()交换两个缓冲区指针. 3. 调用

c++双缓冲技术绘图避免闪烁

当数据量很大时,绘图可能需要几秒钟甚至更长的时间,而且有时还会出现闪烁现象,为了解决这些问题,可采用双缓冲技术来绘图. 双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度.双缓冲实现过程如下: 1.在内存中创建与画布一致的缓冲区 2.在缓冲区画图 3.将缓冲区位图拷贝到当前画布上 4.释放内存缓冲区 在图形图象处理编程过程中,双缓冲是一种基本的技术.我们知道,如果窗体在响应WM_PAINT消息的时候

双缓冲技术讲解

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解>电子工业出版社等. CSDN视频网址:http://edu.csdn.net/lecturer/144 首先要搞清楚计算机运行原理,计算机载运行时是将将最大的任务分解成多个任务,然后一个接一个地执行. 一个典型的例子,每个游戏引擎必须解决的问题是渲染. 当游戏画出用户看到的世界时,比如

控件的重绘和双缓冲技术

处理重绘和无效操作: 重新定制控件行为和外观 Void Invalidate(); void Invalidate ( Rectangle ); //使控件的特定区域无效并且向控件发送绘制的消息; void Update(); //使控件重绘其工作区内的无效区域; void Refresh(); //相当于 this.invalidate(true); this.Update(); 双缓冲技术: 当数据量很大时,绘图可能需要几秒钟甚至更长的时间,而且有时还会出现闪烁现象,为了解决这些问题,可采用

双缓冲技术(Double Buffering)(1、简介和源代码部分)

这一节实在是有些长,翻译完后统计了一下,快到2w字了.考虑到阅读的方便和网络的速度,打算把这节分为5个部分,第一部分为双缓冲技术的一个 简介和所有的代码,如果能够看懂代码,不用看译文也就可以了.第二部分为Plotter控件的公有函数的实现,第三部分为Plotter的事件处理函数的 实现,第四部分为Plotter控件的私有函数实现,第五部分为辅助类PlotSettings的实现. 这里给出一些常用的中英文对照(不一定准确,我这样用的): Rubber band(橡皮筋线,或者橡皮线), pixma

avalon与双缓冲技术

avalon与双缓冲技术 avalon1.5一个重要技术升级是引进异步渲染.异步渲染在游戏界有一个更专业的名字,叫双缓冲.游戏界要刷新界面与我们刷新浏览器视图,面临的问题是一致的.视图是由许多存在套嵌关系的方块组成,它们每一个的改动,都可能引起reflow(其父节点,其父父节点的大小重新计算),这是造成性能问题的关键. 双缓冲技术的主要原理是:当一个动画争先显示时,程序又在改变它,前面的画面还没显示完,程序又要求重新绘制,这样屏幕就会不停闪烁.为了避免闪烁,可以使用双缓冲技术,将要处理的图片都放

Android开发之用双缓冲技术画图

双缓冲技术主要用在绘图,动画效果上,其原理就是:将资源先加载到缓冲区,然后再将缓冲区整个加载到View上面去.双缓冲技术能够有效防止闪烁,提高显示质量. DrawView.java: package com.example.handdraw; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.C

【MFC】MFC绘图不闪烁——双缓冲技术

MFC绘图不闪烁——双缓冲技术[转] 2010-04-30 09:33:33|  分类: VC|举报|字号 订阅 [转自:http://blog.163.com/[email protected]/blog/static/49846449201033093333394/] 在VC/MFC用CDC绘图时,频繁的刷新,屏幕会出现闪烁的现象,CPU时间占用率相当高,绘图效率极低,很容易出现程序崩溃. 所谓双缓冲技术,下面是百度百科的解释: 我们看电视时,看到的屏幕称为OSD层,也就是说,只有在OSD层

基于c#的双缓冲技术画图

导致画面闪烁的关键原因是:.窗口刷新一次的过程中,每一个图元的重绘都会立即显示到窗口,因此整个窗口中,只要是图元所在的位置,都在刷新,而刷新的时间是有差别的,闪烁现象自然会出现.所以说,此时导致窗口闪烁现象的关键因素并不在于Paint事件调用的次数多少,而在于各个图元的重绘. 因此,,当图数目不多时,窗口刷新的位置也不多,窗口闪烁效果并不严重;当图元数目较多时,绘图窗口进行重绘的图元数量增加,绘图窗口每一次刷新都会导致较多的图元重新绘制,窗口的较多位置都在刷新,闪烁现象自然就会越来越严重.特别是