双缓冲绘图技术.docx

上传人:b****4 文档编号:24142218 上传时间:2023-05-24 格式:DOCX 页数:18 大小:20.04KB
下载 相关 举报
双缓冲绘图技术.docx_第1页
第1页 / 共18页
双缓冲绘图技术.docx_第2页
第2页 / 共18页
双缓冲绘图技术.docx_第3页
第3页 / 共18页
双缓冲绘图技术.docx_第4页
第4页 / 共18页
双缓冲绘图技术.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

双缓冲绘图技术.docx

《双缓冲绘图技术.docx》由会员分享,可在线阅读,更多相关《双缓冲绘图技术.docx(18页珍藏版)》请在冰豆网上搜索。

双缓冲绘图技术.docx

双缓冲绘图技术

由双缓冲绘图技术谈起到Delphi源码实现

摘要:

双缓冲绘图技术在Delphi中的实现

关键字:

Delphi,双缓冲,Canvas

作者:

上海翰博数码科技实业有限公司沈小云

说明:

假设读者熟悉VCL

双缓冲绘图也不是什么新技术,简单的说:

在绘图实现时不直接绘在窗口上,而是先绘在内存里,再一起“拷贝”至窗口。

实现起来也不复杂,创建一兼容HDC,在此兼容HDC上绘图,最后拷贝到窗口HDC就行了。

本人前段时间把一C++实现该技术的代码改成了Delphi代码,都是用Win32API写的。

今改成了使用Delphi自带的类,试了一下(窗口类Canvas与TImage的Canvas)。

实现方式大同小异,但不得不提的是在窗口中直接使用Canvas绘图与TImage.Canvas却不相同。

使用TImage.Canvas绘图时,自动使用了双缓冲技术,而窗口的Canvas对像却未实现。

怎么回事呢?

看一下代码吧,“源码面前没有秘密”!

一.TImage类的Canvas

TImage=class(TGraphicControl)

...

propertyCanvas:

TCanvasreadGetCanvas;

...

functionTImage.GetCanvas:

TCanvas;

var

Bitmap:

TBitmap;

begin

ifPicture.Graphic=nilthen

begin

Bitmap:

=TBitmap.Create;

try

Bitmap.Width:

=Width;

Bitmap.Height:

=Height;

Picture.Graphic:

=Bitmap;

finally

Bitmap.Free;

end;

end;

ifPicture.GraphicisTBitmapthen

Result:

=TBitmap(Picture.Graphic).Canvas

else

raiseEInvalidOperation.Create(SImageCanvasNeedsBitmap);

end;

可知TImage.Canvas来自Bitmap.Canvas,好,那来看看TBitmap.Canvas

functionTBitmap.GetCanvas:

TCanvas;

begin

ifFCanvas=nilthen

begin

HandleNeeded;

ifFCanvas=nilthen//possiblerecursion

begin

FCanvas:

=TBitmapCanvas.Create(Self);

FCanvas.OnChange:

=Changed;

FCanvas.OnChanging:

=Changing;

end;

end;

Result:

=FCanvas;

end;

显而易见TBitmap.Canvas=TBitmapCanvas.Create;也就是说TImage.Canvas=TBitmapCanvas.Create.即使用TImage.Canvas绘图时,实际是在TBitmapCanvas上绘图的。

让我们再来看看TBitmapCanvas类:

TBitmapCanvas=class(TCanvas)

private

FBitmap:

TBitmap;

FOldBitmap:

HBITMAP;

FOldPalette:

HPALETTE;

procedureFreeContext;

protected

procedureCreateHandle;override;

public

constructorCreate(ABitmap:

TBitmap);

destructorDestroy;override;

end;

关注一下CreateHandle函数:

procedureTBitmapCanvas.CreateHandle;

var

H:

HBITMAP;

begin

ifFBitmap<>nilthen

begin

Lock;

try

FBitmap.HandleNeeded;

DeselectBitmap(FBitmap.FImage.FHandle);

//!

!

DeselectBitmap(FBitmap.FImage.FMaskHandle);

FBitmap.PaletteNeeded;

H:

=CreateCompatibleDC(0);

ifFBitmap.FImage.FHandle<>0then

FOldBitmap:

=SelectObject(H,FBitmap.FImage.FHandle)else

FOldBitmap:

=0;

ifFBitmap.FImage.FPalette<>0then

begin

FOldPalette:

=SelectPalette(H,FBitmap.FImage.FPalette,True);

RealizePalette(H);

end

else

FOldPalette:

=0;

Handle:

=H;

BitmapCanvasList.Add(Self);

finally

Unlock;

end;

end;

end;

读起来也不困难,FBitmap是Create构造函数传进来的。

而我们应该关注的代码位于斜体部份,也很好理解:

创建兼容DC,并选进设备。

要的就是这个效果,现在知道为什么使用TImage.Canvas来绘图是使用的双缓冲技术的了吧?

那么这个兼容DC是如何从内存“拷贝”到窗口的呢?

我们使用上面的分析方法,当TImage基类TGraphicControl收到WM_PAINT消息时,将执行下面的代码:

procedureTGraphicControl.WMPaint(varMessage:

TWMPaint);

begin

ifMessage.DC<>0then

begin

Canvas.Lock;

try

Canvas.Handle:

=Message.DC;

try

Paint;

finally

Canvas.Handle:

=0;

end;

finally

Canvas.Unlock;

end;

end;

end;

(在此先仅关注Paint函数)

而TImage覆盖了此Paint虚函数:

procedureTImage.Paint;

var

Save:

Boolean;

begin

ifcsDesigninginComponentStatethen

withinheritedCanvasdo

begin

Pen.Style:

=psDash;

Brush.Style:

=bsClear;

Rectangle(0,0,Width,Height);

end;

Save:

=FDrawing;

FDrawing:

=True;

try

withinheritedCanvasdo//祖先的Canvas

StretchDraw(DestRect,Picture.Graphic);

finally

FDrawing:

=Save;

end;

end;

抛开枝节,关注两个地方,一是斜体部份的Canvas对像,二是StrectchDraw函数。

先看看此Canvas对像,它被显示声明为基类的Canvas对像。

不得不提,此Canvas.Handle即句柄的赋值代码:

procedureTGraphicControl.WMPaint(varMessage:

TWMPaint);

begin

ifMessage.DC<>0then

begin

Canvas.Lock;

try

Canvas.Handle:

=Message.DC;

try

Paint;

finally

Canvas.Handle:

=0;

end;

finally

Canvas.Unlock;

end;

end;

end;

是消息传递进来的,这里的DC为此TGraphicControl.Parent的DC。

至于如何传递进来的请参考《VCL构架剖析》,在此不费话了。

再看第二个关注点StrectDraw函数:

procedureTCanvas.StretchDraw(constRect:

TRect;Graphic:

TGraphic);

begin

ifGraphic<>nilthen

begin

Changing;

RequiredState(csAllValid);

Graphic.Draw(Self,Rect);

Changed;

end;

end;

这里的Graphic是什么呢?

这里是TBitmap!

看看第一块代码。

那再看TBitmap.Draw函数吧:

procedureTBitmap.Draw(ACanvas:

TCanvas;constRect:

TRect);

var

OldPalette:

HPalette;

RestorePalette:

Boolean;

DoHalftone:

Boolean;

Pt:

TPoint;

BPP:

Integer;

MaskDC:

HDC;

Save:

THandle;

begin

withRect,FImagedo

begin

ACanvas.RequiredState(csAllValid);

PaletteNeeded;

OldPalette:

=0;

RestorePalette:

=False;

ifFPalette<>0then

begin

OldPalette:

=SelectPalette(ACanvas.FHandle,FPalette,True);

RealizePalette(ACanvas.FHandle);

RestorePalette:

=True;

end;

BPP:

=GetDeviceCaps(ACanvas.FHandle,BITSPIXEL)*

GetDeviceCaps(ACanvas.FHandle,PLANES);

DoHalftone:

=(BPP<=8)and(BPP<(FDIB.dsbm.bmBitsPixel*FDIB.dsbm.bmPlanes));

ifDoHalftonethen

begin

GetBrushOrgEx(ACanvas.FHandle,pt);

SetStretchBltMode(ACanvas.FHandle,HALFTONE);

SetBrushOrgEx(ACanvas.FHandle,pt.x,pt.y,@pt);

endelseifnotMonochromethen

SetStretchBltMode(ACanvas.Handle,STRETCH_DELETESCANS);

try

{CallMaskHandleNeededpriortocreatingthecanvashandlesince

itcausesFreeContexttobecalled.}

ifTransparentthenMaskHandleNeeded;

Canvas.RequiredState(csAllValid);

ifTransparentthen

begin

Save:

=0;

MaskDC:

=0;

try

MaskDC:

=GDICheck(CreateCompatibleDC(0));

Save:

=SelectObject(MaskDC,FMaskHandle);

TransparentStretchBlt(ACanvas.FHandle,Left,Top,Right-Left,

Bottom-Top,Canvas.FHandle,0,0,FDIB.dsbm.bmWidth,

FDIB.dsbm.bmHeight,MaskDC,0,0);

finally

ifSave<>0thenSelectObject(MaskDC,Save);

ifMaskDC<>0thenDeleteDC(MaskDC);

end;

end

else

StretchBlt(ACanvas.FHandle,Left,Top,Right-Left,Bottom-Top,

Canvas.FHandle,0,0,FDIB.dsbm.bmWidth,

FDIB.dsbm.bmHeight,ACanvas.CopyMode);

finally

ifRestorePalettethen

SelectPalette(ACanvas.FHandle,OldPalette,True);

end;

end;

不要再深挖了,斜体部份很明了,功能就是将绘图内容从内存拷贝至窗口。

ACanvas.FHandle即上面所说的消息传递进来的HDC。

(ACanvas是TImage的祖先TGraphicControl的内部对像,Canvas在此为TBitmapCanvas实例)。

可能有点乱,因为我整理好了之后,再次阅读时,自已也迷糊了,仔细多看两遍吧。

再提一下:

TGraphicControl.Canvas与TImage.Canvas是两个实例,虽然TImage继承自TGraphicControl。

好了,我们再来看看为何使用窗口Canvas属性进行绘画时,没有使用双缓冲技术吧

二.窗口类的Canvas

其实也不能决对说窗口Canvas没有使用双缓冲技术,它有使用,但有限制。

条件是在将窗口TForm.DoubleBuffered设为TRUE的前提下,在Paint事件函数里使用Canvas对像进行绘图动作。

下面还是按照上面的方法来找出其中的缘由。

先看一下TCustomForm.WMPaint消息处理函数:

procedureTCustomForm.WMPaint(varMessage:

TWMPaint);

var

DC:

HDC;

PS:

TPaintStruct;

begin

ifnotIsIconic(Handle)then

begin

ControlState:

=ControlState+[csCustomPaint];

inherited;

ControlState:

=ControlState-[csCustomPaint];

end

else

begin

DC:

=BeginPaint(Handle,PS);

DrawIcon(DC,0,0,GetIconHandle);

EndPaint(Handle,PS);

end;

end;

这个简单,基本只用考滤斜体部份代码,即调用基类同名函数,在此要追溯到TWinControl.WMPaint函数:

procedureTWinControl.WMPaint(varMessage:

TWMPaint);

var

DC,MemDC:

HDC;

MemBitmap,OldBitmap:

HBITMAP;

PS:

TPaintStruct;

begin

ifnotFDoubleBufferedor(Message.DC<>0)then

begin

ifnot(csCustomPaintinControlState)and(ControlCount=0)then

inherited

else

PaintHandler(Message);

end

else

begin

DC:

=GetDC(0);

MemBitmap:

=CreateCompatibleBitmap(DC,ClientRect.Right,ClientRect.Bottom);

ReleaseDC(0,DC);

MemDC:

=CreateCompatibleDC(0);

OldBitmap:

=SelectObject(MemDC,MemBitmap);

try

DC:

=BeginPaint(Handle,PS);

Perform(WM_ERASEBKGND,MemDC,MemDC);

Message.DC:

=MemDC;

WMPaint(Message);

Message.DC:

=0;

BitBlt(DC,0,0,ClientRect.Right,ClientRect.Bottom,MemDC,0,0,SRCCOPY);

EndPaint(Handle,PS);

finally

SelectObject(MemDC,OldBitmap);

DeleteDC(MemDC);

DeleteObject(MemBitmap);

end;

end;

end;

好,先看下面的斜体部份,若将TForm.DoubleBuffered设为TRUE,那么将创建兼容DC即使用双缓冲技术。

完了之后还是将会调用此函数,这时将会执行第一个斜体部份代码。

也就是说,不管是否设置Form.DoubleBuffered为何值,始终会执行PaintHandler函数。

只不过传递的参数的属性(DC)不同罢了。

那就看看PaintHandler都干了些什么吧:

procedureTWinControl.PaintHandler(varMessage:

TWMPaint);

var

I,Clip,SaveIndex:

Integer;

DC:

HDC;

PS:

TPaintStruct;

begin

DC:

=Message.DC;

ifDC=0thenDC:

=BeginPaint(Handle,PS);

try

ifFControls=nilthenPaintWindow(DC)else

begin

SaveIndex:

=SaveDC(DC);

Clip:

=SimpleRegion;

forI:

=0toFControls.Count-1do

withTControl(FControls[I])do

if(Visibleor(csDesigninginComponentState)and

not(csNoDesignVisibleinControlStyle))and

(csOpaqueinControlStyle)then

begin

Clip:

=ExcludeClipRect(DC,Left,Top,Left+Width,Top+Height);

ifClip=NullRegionthenBreak;

end;

ifClip<>NullRegionthenPaintWindow(DC);

RestoreDC(DC,SaveIndex);

end;

PaintControls(DC,nil);

finally

ifMessage.DC=0thenEndPaint(Handle,PS);

end;

end;

PaintWindow是虚函数,TCustomForm覆盖了它:

procedureTCustomForm.PaintWindow(DC:

HDC);

begin

FCanvas.Lock;

try

FCanvas.Handle:

=DC;

try

ifFDesigner<>nilthenFDesigner.PaintGridelsePaint;

finally

FCanvas.Handle:

=0;

end;

finally

FCanvas.Unlock;

end;

end;

可以看到,DC值赋给了FCanvas,触发了Paint事件。

由此可以知,只有在将TForm.DoubleBuffered设为TRUE的情况下,并在OnPaint事件中调用Canvas属性时,才会使用双缓冲技术。

那在TForm的其它事件中使用Canvas绘图是什么情况呢:

constructorTCustomForm.CreateNew(AOwner:

TComponent;Dummy:

Integer);

begin

inheritedCreate(AOwner);

ControlStyle:

=[csAcceptsControls,csCaptureMouse,csClickEvents,

csSetCaption,csDoubleClicks];

Left:

=0;

Top:

=0;

Width:

=320;

Height:

=240;

FIcon:

=TIcon.Create;

FIcon.Width:

=GetSystemMetrics(SM_CXSMICON);

FIcon.Height:

=GetSystemMetrics(SM_CYSMICON);

FIcon.OnChange:

=IconChanged;

FCanvas:

=TControlCanvas.Create;

FCanvas.Control:

=Self;

FBorderIcons:

=[biSystemMenu,biMinimize,biMaximize];

FBorderStyle:

=bsSizeable;

再看一下TControlCanvas类的定义:

procedureTControlCanvas.CreateHandle;

begin

ifFControl=niltheninheritedCreateHandleelse

begin

ifFDeviceContext=0then

begin

withCanvasList.LockListdo

try

ifCount>=CanvasListCacheSizethenFreeDeviceContext;

FDeviceContext:

=FControl.GetDeviceContext(FWindowHandle);

Add(Self);

finally

Ca

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 总结汇报 > 学习总结

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1