2017-08-11 79 views
3

请考虑下面的代码:TBitmap后不相关的图形代码失去裁剪区域

type 
    TBaseControl = class(TWinControl) 
    private 
    FBitmap : TBitmap; 
    public 
    constructor Create(AOwner : TComponent); override; 
    procedure DrawBorder; 
    end; 

    TForm1 = class(TForm) 
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 
    private 
    public 
    end; 

var 
    Form1: TForm1; 
    NewC : TBaseControl; 

implementation 

{$R *.dfm} 

constructor TBaseControl.Create(AOwner : TComponent); 
begin 
    inherited Create(AOwner); 
    FBitmap := TBitmap.Create; 
    FBitmap.PixelFormat := pf24bit; 
    FBitmap.SetSize(100,100); 
end; 

procedure TBaseControl.DrawBorder; 
var 
    Region : HRGN; 
    ContentRect : TRect; 
begin 
    // Almost like a Client Area of a control 
    ContentRect := Rect(10,10,FBitmap.Width - 10,FBitmap.Height - 10); 

    // Create clipping region on FBitmap with ContentRect being excluded 
    Region := CreateRectRgnIndirect(Rect(0,0,Width,Height)); 
    SelectClipRgn(FBitmap.Canvas.Handle,Region); 
    ExcludeClipRect(FBitmap.Canvas.Handle,ContentRect.Left,ContentRect.Top, 
        ContentRect.Right,ContentRect.Bottom); 
    DeleteObject(Region); 

    // Do Pre-drawing 
    FBitmap.Canvas.Brush.Style := bsSolid; 
    FBitmap.Canvas.Brush.Color := clRed; 
    FBitmap.Canvas.FillRect(Rect(0,0,FBitmap.Width,FBitmap.Height)); 


    // Will comment out one of these statements 
    // The graphics one (.Caption) will cause the clipping to be lost. Any 
    // graphics code will do it as long as it is not related to FBitmap 
    // ======================================================================== 
    Form1.Caption := 'You have just lost your Bitmap''s clipping'; 
    // ----- 
    Form1.Tag := Random(1000); 
    // ======================================================================== 


    // Do some drawing afterwards 
    FBitmap.Canvas.Brush.Color := clGreen; 
    FBitmap.Canvas.FillRect(Rect(5,5,FBitmap.Width - 5,FBitmap.Height - 5)); 

    // Want to see what it looks like 
    FBitmap.SaveToFile('d:\test.bmp'); 
    // Test the tag setting 
    ShowMessage(InttoStr(Form1.Tag)); 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    // Create an instance of TBaseControl 
    NewC := TBaseControl.Create(Self); 
    NewC.SetBounds(0,0,200,200); 
    NewC.Parent := Self; 
    // Tell it to draw 
    NewC.DrawBorder; 
end; 

DrawBorder,如果我只设置Form1的标签没有字幕被设置,那么FBitmap的剪辑区域保持和在整个绘图代码中受到尊重。 FBitmap看起来就像这样:

enter image description here

但如果Form1的标题设置然后FBitmap将失去它的剪切区域,看起来像这样:

enter image description here

如此看来,后Form1的标题是设置FBitmap失去了它的剪辑区域。当发生这种情况时,WindowOrigins(通过SetWindowOrgEx设置)也会丢失。

+0

该位图丢失了其画布(在D2009中该表单标题更改后,其句柄将变为0)。控制权未被重新创建。你有没有试图牺牲一只小棕松鼠或类似的树木动物?这真的没有意义。 – Victoria

+1

@Victoria当你考虑到VCL缓存GDI资源并且经常(在消息处理期间)释放未被主动锁定的画布HDC时,它是有意义的。不要指望GDI设置随着时间的推移而被保留下来。只要你需要画东西就重置它们。由'DrawBorder'设置的区域在执行返回主消息循环后可能无效(例如由于'Caption'更改触发重绘) –

回答

2

在阅读了上述维多利亚和雷米的评论之后,我意识到锁定画布可能会有所帮助,因此我尝试在FBitmap.Canvas.LockFBitmap.Canvas.UnLock之间包装绘图代码,这似乎解决了问题。

procedure TBaseControl.DrawBorder; 
var 
    Region : HRGN; 
    ContentRect : TRect; 
begin 
    FBitmap.Canvas.Lock; 

    // ....All the drawing code------------------- 
    // ....All the drawing code------------------- 

    FBitmap.Canvas.UnLock; 

    // Want to see what it looks like 
    FBitmap.SaveToFile('d:\test.bmp'); 
    // Test the tag setting 
    ShowMessage(InttoStr(Form1.Tag)); 
end; 
+2

我只是将它放在'try..finally'块中(这里有点偏执,但仍然)。 – Victoria