德尔福/ GDI +:是设备上下文创建时/销毁?(Delphi/GDI+: When is a De

2019-08-31 20:11发布

通常情况下使用GDI +在Delphi中你可以OnPaint事件中使用TPaintBox,和油漆:

procedure TForm1.PaintBox1Paint(Sender: TObject);
var
   g: TGPGraphics;
begin
   g := TGPGraphics.Create(PaintBox1.Canvas.Handle);
   try
      g.DrawImage(FSomeImage, 0, 0);
   finally
      g.Free;
   end;
end;

这种范式的问题是,创建一个销毁Graphics对象每一次都是浪费,效果不好。 此外,也有一些在GDI速效结构+当你有一个持久的Graphics对象,你只能使用。

这个问题,当然,是我什么时候可以创建一个图形对象? 我需要知道当手柄变为可用,然后当它不再有效。 我需要这些信息,所以我可以创建和销毁我的Graphics对象。


解决方案尝试Nº1

我可以通过创建它,当真正需要它解决创新的问题-在第一次油漆周期被称为:

procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
   if FGraphics = nil then
      FGraphics := TGPGraphics.Create(PaintBox1.Canvas.Handle);

   FGraphics.DrawImage(FSomeImage, 0, 0);
end;

但我知道什么时候该设备上下文不再有效,所以我可以毁了我的FGraphcis对象,因此它是重新创建需要它的下一次。 如果由于某种原因,TPaintBox的设备上下文被重建,我会在一个无效的设备上下文下一次的OnPaint被称为绘图。

在德尔福的预期机制,当TPaintBox设备上下文句柄创建,销毁或重新创建我知道的是什么?

Answer 1:

你不能用标准TPaintBox因为TPaintBox有型TControlCanvas,无有关此问题的成员是这些的画布:

TControlCanvas = class(TCanvas)
private
  ...
  procedure SetControl(AControl: TControl);
protected
  procedure CreateHandle; override;
public
  procedure FreeHandle;
  ...
  property Control: TControl read FControl write SetControl;
end;

问题是,FreeHandle和SetControl不虚。

但是:在TControlCanvas创建并分配在这里:

 constructor TGraphicControl.Create(AOwner: TComponent);
 begin
   inherited Create(AOwner);
   FCanvas := TControlCanvas.Create;
   TControlCanvas(FCanvas).Control := Self;
 end;

所以,你可以做的是创造一个下降TMyControlCanvas确实有虚方法,并指派画布这样的TMyPaintBox:

 constructor TMyPaintBox.Create(AOwner: TComponent);
 begin
   inherited Create(AOwner);
   FCanvas.Free;
   FCanvas := TMyControlCanvas.Create;
   TMyControlCanvas(FCanvas).Control := Self;
 end;

然后你可以使用的方法TMyControlCanvas动态创建和摧毁你的TGPGraphics。

这应该让你去。

--jeroen



Answer 2:

检测创作是容易的。 只是覆盖CreateHandle在子TControlCanvas ,并把你的到位默认之一的吉荣的回答证明 。 检测破坏是困难。

为了避免这个问题的方法之一是检查TGpGraphics手柄是否等于漆箱的把手,这样,而不是检测时的瞬间设备上下文被释放,你只需选中你需要知道之前。

if not Assigned(FGraphics)
    or (FGraphics.GetHDC <> PaintBox1.Canvas.Handle) then begin
  FGraphics.Free;
  FGraphics := TGpGraphics.Create(PaintBox1.Canvas.Handle);
end;

这可能是不可靠的,虽然, 办理值是容易被重复使用,所以虽然HDC值可能是两次检查之间的相同,也不能保证它仍然是指同一个操作系统的设备上下文对象。


TCanvas基类不会清除自己的Handle属性,所以必须从外部进行任何无效的画布。 TControlCanvas清除其Handle物业公司当Control属性,都会重新分配,但通常只有当创建了控制,因为发生TControlCanvas实例很少共享。 然而, TControlCanvas情况下,从设备上下文的区处理保持在工作CanvasList 。 当其中一人需要一个DC(在TControlCanvas.CreateHandle ),它调用FreeDeviceContext ,以腾出空间在画布缓存它要创建的句柄。 该函数调用(非虚拟) FreeHandle方法。 缓存大小为4(见CanvasListCacheSize ),所以如果你有几个后代TCustomControlTGraphicControl在你的程序中,机会是很高,你会得到更多的时候比他们四人需要一次重新绘制高速缓存未命中。

TControlCanvas.FreeHandle不是虚拟的,它不调用任何虚方法。 虽然你可以作出这样的类的子类,并给它的虚拟方法时,VCL的其余部分将继续调用非虚方法,无视任何新增项目。


不要试图一个设备上下文被释放时进行检测,你可能会使用不同的TGpGraphics构造会更好。 使用的一个,需要一个窗口句柄而不是DC句柄,例如。 窗口句柄破坏是很容易被发现。 对于一次性的解决方案,指定自己的方法到TPaintBox.WindowProc财产和监视wm_Destroy消息。 如果你经常这样做,然后做一个子类,并覆盖DestroyWnd



Answer 3:

性能打你需要创建/销毁图形对象是最小的。 它是由远在第一时间使用GDI +的绘图命令的性能损失抵消。 无论其中,国际海事组织,是值得担忧的,当涉及到绘图的用户界面,因为用户不会注意到反正。 坦率地说,它可以是非常不方便的,试图携带一个图形对象,跟踪变化的DC句柄(特别是如果你封装你自己的一组类中的图形程序)。

如果你需要缓存的位图,你可以考虑做的是创建要与GDI缓存位图+(让你想要的大小合适&W /不管反锯齿设置),将其保存到内存流,然后当你需要它,从流加载它,并使用好醇” BitBlt的绘制。 这将是多少,比使用Graphics.DrawImage快得多。 我说快几个数量级。



Answer 4:

procedure TGraphicControl.WMPaint(var Message: TWMPaint);
begin
  if Message.DC <> 0 then
  begin
    Canvas.Lock;
    try
      Canvas.Handle := Message.DC;
      try
        Paint;
      finally
        Canvas.Handle := 0;
      end;
    finally
      Canvas.Unlock;
    end;
  end;
end;

Canvas.Handle := Message.DC;


文章来源: Delphi/GDI+: When is a Device Context created/destroyed?