如何避免闪烁?(100分)

  • 主题发起人 主题发起人 刘晗
  • 开始时间 开始时间

刘晗

Unregistered / Unconfirmed
GUEST, unregistred user!
在一个大Image1上有一个小Image2;
当图象(Image1)移动时(Image1不动,是其上图象动)Image2总要闪烁。怎么办?
听说有所谓‘脱屏位图’能否管用?
是不是非要用DirectX才行?
 
刷新快一点
 
不要用image进行图象平移,用copy象素的方法可以避免,
主要是Bitblt函数的运用,查一下,自己可以搞定!
 
用DirectX肯定可以,不过要难一些,拷贝像素的方法对于小图像运算也行。
 
试试这样:

移动 image2 前先将其 visible 属性设为 false ,移到指定地点后再将 image2 的

属性设为 true 。
 
我用copyRect 作的 应该算是拷贝像素吧
Image1.Canvas.CopyRect(Rect(0,0,Width,Height),
Image1.Canvas,Rect(i,j,i+width,j+Height));
还是不行.
 
刘晗说的没错!
 
CoyRect还是对象素的整块复制,而BitBlt一次只复制一个象素!这样吧,给你一
段代码,是关于如何生成动画,请仔细研究!

unit MainFrm;

interface

uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, Menus, Stdctrls;

{$R SPRITES.RES } // Link in the bitmaps

type

TSprite = class
private
FWidth: integer;
FHeight: integer;
FLeft: integer;
FTop: integer;
FAndImage, FOrImage: TBitMap;
public
property Top: Integer read FTop write FTop;
property Left: Integer read FLeft write FLeft;
property Width: Integer read FWidth write FWidth;
property Height: Integer read FHeight write FHeight;
constructor Create;
destructor Destroy; override;
end;

TMainForm = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
BackGnd1, BackGnd2: TBitMap;
Sprite: TSprite;
GoLeft,GoRight,GoUp,GoDown: boolean;
procedure MyIdleEvent(Sender: TObject; var Done: Boolean);
procedure DrawSprite;
end;

const

BackGround = 'BACK2.BMP';

var
MainForm: TMainForm;

implementation

{$R *.DFM}

constructor TSprite.Create;
begin
inherited Create;
{ Create the bitmaps to hold the sprite images that will
be used for performing the AND/OR operation to create animation }
FAndImage := TBitMap.Create;
FAndImage.LoadFromResourceName(hInstance, 'AND');

FOrImage := TBitMap.Create;
FOrImage.LoadFromResourceName(hInstance, 'OR');

Left := 0;
Top := 0;
Height := FAndImage.Height;
Width := FAndImage.Width;

end;

destructor TSprite.Destroy;
begin
FAndImage.Free;
FOrImage.Free;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
// Create the original background image
BackGnd1 := TBitMap.Create;
with BackGnd1 do
begin
LoadFromResourceName(hInstance, 'BACK');
Parent := nil;
SetBounds(0, 0, Width, Height);
end;

// Create a copy of the background image
BackGnd2 := TBitMap.Create;
BackGnd2.Assign(BackGnd1);


// Create a sprite image
Sprite := TSprite.Create;

// Initialize the direction variables
GoRight := true;
GoDown := true;
GoLeft := false;
GoUp := false;

{ Set the application's OnIdle event to MyIdleEvent which will start
the sprite moving }
Application.OnIdle := MyIdleEvent;
// Adjust the form's client width/height
ClientWidth := BackGnd1.Width;
ClientHeight := BackGnd1.Height;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
// Free all objects created in the form's create constructor
BackGnd1.Free;
BackGnd2.Free;
Sprite.Free;
end;

procedure TMainForm.MyIdleEvent(Sender: TObject; var Done: Boolean);
begin
DrawSprite;
{ Allow OnIdle to be called even when no messages are in the
application's message queue}
Done := False;
end;

procedure TMainForm.DrawSprite;
var
OldBounds: TRect;
begin

// Save the sprite's bounds in OldBounds
with OldBounds do
begin
Left := Sprite.Left;
Top := Sprite.Top;
Right := Sprite.Width;
Bottom := Sprite.Height;
end;

{ Now change the sprites bounds so that it moves in one direction
or changes direction when it comes in contact with the form's
boundries }
with Sprite do
begin
if GoLeft then
if Left > 0 then
Left := Left - 1
else begin
GoLeft := false;
GoRight := true;
end;

if GoDown then
if (Top + Height) < self.ClientHeight then
Top := Top + 1
else begin
GoDown := false;
GoUp := true;
end;

if GoUp then
if Top > 0 then
Top := Top - 1
else begin
GoUp := false;
GoDown := true;
end;

if GoRight then
if (Left + Width) < self.ClientWidth then
Left := Left + 1
else begin
GoRight := false;
GoLeft := true;
end;
end;

{ Erase the original drawing of the sprite on BackGnd2 by copying
a rectangle from BackGnd1 }
with OldBounds do
BitBlt(BackGnd2.Canvas.Handle, Left, Top, Right, Bottom,
BackGnd1.Canvas.Handle, Left, Top, SrcCopy);

{ Now draw the sprite onto the off-screen bitmap. By performing the
drawing in an off-screen bitmap, the flicker is eliminated. }
with Sprite do
begin
{ Now create a black hole where the sprite first existed by And-ing
the FAndImage onto BackGnd2 }
BitBlt(BackGnd2.Canvas.Handle, Left, Top, Width, Height,
FAndImage.Canvas.Handle, 0, 0, SrcAnd);
// Now fill in the black hole with the sprites original colors
BitBlt(BackGnd2.Canvas.Handle, Left, Top, Width, Height,
FOrImage.Canvas.Handle, 0, 0, SrcPaint);
end;

{ Copy the sprite at it's new location to the form's Canvas. A
rectangle slightly larger than the sprite is needed
to effectively erase the sprite by over-writing it, and draw the
new sprite at the new location with a single BitBlt call }
with OldBounds do
BitBlt(Canvas.Handle, Left - 2, Top - 2, Right + 2, Bottom + 2,
BackGnd2.Canvas.Handle, Left - 2, Top - 2, SrcCopy);

end;

procedure TMainForm.FormPaint(Sender: TObject);
begin
// Draw the background image whenever the form gets painted
BitBlt(Canvas.Handle, 0, 0, ClientWidth, ClientHeight,
BackGnd1.Canvas.Handle, 0, 0, SrcCopy);
end;

end.
 
CoyRect还是对象素的整块复制,而BitBlt一次只复制一个象素!这样吧,给你一
段代码,是关于如何生成动画,请仔细研究!

unit MainFrm;

interface

uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, Menus, Stdctrls;

{$R SPRITES.RES } // Link in the bitmaps

type

TSprite = class
private
FWidth: integer;
FHeight: integer;
FLeft: integer;
FTop: integer;
FAndImage, FOrImage: TBitMap;
public
property Top: Integer read FTop write FTop;
property Left: Integer read FLeft write FLeft;
property Width: Integer read FWidth write FWidth;
property Height: Integer read FHeight write FHeight;
constructor Create;
destructor Destroy; override;
end;

TMainForm = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
BackGnd1, BackGnd2: TBitMap;
Sprite: TSprite;
GoLeft,GoRight,GoUp,GoDown: boolean;
procedure MyIdleEvent(Sender: TObject; var Done: Boolean);
procedure DrawSprite;
end;

const

BackGround = 'BACK2.BMP';

var
MainForm: TMainForm;

implementation

{$R *.DFM}

constructor TSprite.Create;
begin
inherited Create;
{ Create the bitmaps to hold the sprite images that will
be used for performing the AND/OR operation to create animation }
FAndImage := TBitMap.Create;
FAndImage.LoadFromResourceName(hInstance, 'AND');

FOrImage := TBitMap.Create;
FOrImage.LoadFromResourceName(hInstance, 'OR');

Left := 0;
Top := 0;
Height := FAndImage.Height;
Width := FAndImage.Width;

end;

destructor TSprite.Destroy;
begin
FAndImage.Free;
FOrImage.Free;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
// Create the original background image
BackGnd1 := TBitMap.Create;
with BackGnd1 do
begin
LoadFromResourceName(hInstance, 'BACK');
Parent := nil;
SetBounds(0, 0, Width, Height);
end;

// Create a copy of the background image
BackGnd2 := TBitMap.Create;
BackGnd2.Assign(BackGnd1);


// Create a sprite image
Sprite := TSprite.Create;

// Initialize the direction variables
GoRight := true;
GoDown := true;
GoLeft := false;
GoUp := false;

{ Set the application's OnIdle event to MyIdleEvent which will start
the sprite moving }
Application.OnIdle := MyIdleEvent;
// Adjust the form's client width/height
ClientWidth := BackGnd1.Width;
ClientHeight := BackGnd1.Height;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
// Free all objects created in the form's create constructor
BackGnd1.Free;
BackGnd2.Free;
Sprite.Free;
end;

procedure TMainForm.MyIdleEvent(Sender: TObject; var Done: Boolean);
begin
DrawSprite;
{ Allow OnIdle to be called even when no messages are in the
application's message queue}
Done := False;
end;

procedure TMainForm.DrawSprite;
var
OldBounds: TRect;
begin

// Save the sprite's bounds in OldBounds
with OldBounds do
begin
Left := Sprite.Left;
Top := Sprite.Top;
Right := Sprite.Width;
Bottom := Sprite.Height;
end;

{ Now change the sprites bounds so that it moves in one direction
or changes direction when it comes in contact with the form's
boundries }
with Sprite do
begin
if GoLeft then
if Left > 0 then
Left := Left - 1
else begin
GoLeft := false;
GoRight := true;
end;

if GoDown then
if (Top + Height) < self.ClientHeight then
Top := Top + 1
else begin
GoDown := false;
GoUp := true;
end;

if GoUp then
if Top > 0 then
Top := Top - 1
else begin
GoUp := false;
GoDown := true;
end;

if GoRight then
if (Left + Width) < self.ClientWidth then
Left := Left + 1
else begin
GoRight := false;
GoLeft := true;
end;
end;

{ Erase the original drawing of the sprite on BackGnd2 by copying
a rectangle from BackGnd1 }
with OldBounds do
BitBlt(BackGnd2.Canvas.Handle, Left, Top, Right, Bottom,
BackGnd1.Canvas.Handle, Left, Top, SrcCopy);

{ Now draw the sprite onto the off-screen bitmap. By performing the
drawing in an off-screen bitmap, the flicker is eliminated. }
with Sprite do
begin
{ Now create a black hole where the sprite first existed by And-ing
the FAndImage onto BackGnd2 }
BitBlt(BackGnd2.Canvas.Handle, Left, Top, Width, Height,
FAndImage.Canvas.Handle, 0, 0, SrcAnd);
// Now fill in the black hole with the sprites original colors
BitBlt(BackGnd2.Canvas.Handle, Left, Top, Width, Height,
FOrImage.Canvas.Handle, 0, 0, SrcPaint);
end;

{ Copy the sprite at it's new location to the form's Canvas. A
rectangle slightly larger than the sprite is needed
to effectively erase the sprite by over-writing it, and draw the
new sprite at the new location with a single BitBlt call }
with OldBounds do
BitBlt(Canvas.Handle, Left - 2, Top - 2, Right + 2, Bottom + 2,
BackGnd2.Canvas.Handle, Left - 2, Top - 2, SrcCopy);

end;

procedure TMainForm.FormPaint(Sender: TObject);
begin
// Draw the background image whenever the form gets painted
BitBlt(Canvas.Handle, 0, 0, ClientWidth, ClientHeight,
BackGnd1.Canvas.Handle, 0, 0, SrcCopy);
end;

end.
 
可以尝试 BitBlt(DestCanvas.Handle, xPos, yPos, Width, Height, SrcCanvas.Handle, xPos, yPos, SRCCOPY)
不过要注意正确获得 TImage.Canvas.Handle,不然无法正确在所要位置输出图像。
 
bitblt() 是api,copyrect()是对其封装(也许);
其实闪烁的本质是GDI在短时间内连续画图产生.
应在内存中画好再一次性画到屏幕上.
这也是directx的方法.
我用delphix已解决了;


 
所谓“脱屏位图”是指在内存中生成的一个图形缓冲,你的绘图操作在内存中进行,
然后再“贴”到前台的屏幕上。
具体的操作是:
1、在内存中生成一个TBitmap对象。(一般来讲,此TBitmap要和前景的Canvas有相同的
宽度和高度)
2、在TBitmap的Canvas上画图。
3、用Canvas.Draw(0,0,TBitmap的实例)将内存位图贴到前景。

如果你不断地贴,你会发现不会有一点闪烁。
<font color=red>Tip:不要试图在内存中直接建立一个Canvas,那样地话无法保存你地绘图内容。</font>
 
接受答案了.
 
后退
顶部