简体   繁体   English

我可以通过覆盖GetPixel / SetPixel方法以某种方式“检测”Graphics.TBitmapCanvas,这些方法特定于TBitmap的画布吗?

[英]Can I somehow “instrument” Graphics.TBitmapCanvas with overriden GetPixel/SetPixel methods, which are specific to TBitmap's canvas?

As we know, working with TBitmap 's pixels ( Bitmap.Canvas.Pixels[X,Y] ) is very slow in the out-of-box VCL. 我们知道,在开箱即用的VCL中使用TBitmap的像素( Bitmap.Canvas.Pixels[X,Y] )非常慢。 This has been caused by getter and setter of Pixels property inherited from TCanvas , which encapsulates general WinGDI DC object and is not specific to MemDC of bitmap. 这是由继承自TCanvasPixels属性的getter和setter引起的,它封装了一般的WinGDI DC对象,并不特定于位图的MemDC。

For the DIB section-based bitmaps ( bmDIB ) a well-known workaround exists, however I do not see the way to integrate the proper getter/setter in the VCL TBitmap class (besides direct modification of library code, which proven to be real pain in the stern when it comes to compiling against different VCL versions). 对于基于DIB部分的位图( bmDIB ),存在一个众所周知的解决方法 ,但是我没有看到在VCL TBitmap类中集成正确的getter / setter的方法(除了直接修改库代码,这被证明是真正的痛苦在编译不同的VCL版本时,在船尾 )。

Please advise if there is some hackish way to reach TBitmapCanvas class and inject overriden methods into it. 请告知是否有一些hackish方式到达TBitmapCanvas类并将override方法注入其中。

I'm sure it could be done more elegantly, but here's what you ask for implemented using a class helper to crack the private members: 我确信它可以更优雅地完成,但是这是你要求使用类助手实现破解私有成员的要求:

unit BitmapCanvasCracker;

interface

uses
  SysUtils, Windows, Graphics;

implementation

procedure Fail;
begin
  raise EAssertionFailed.Create('Fixup failed.');
end;

procedure PatchCode(Address: Pointer; const NewCode; Size: Integer);
var
  OldProtect: DWORD;
begin
  if not VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then begin
    Fail;
  end;
  Move(NewCode, Address^, Size);
  FlushInstructionCache(GetCurrentProcess, nil, 0);
  if not VirtualProtect(Address, Size, OldProtect, @OldProtect) then begin
    Fail;
  end;
end;

type
  PInstruction = ^TInstruction;
  TInstruction = packed record
    Opcode: Byte;
    Offset: Integer;
  end;

procedure RedirectProcedure(OldAddress, NewAddress: Pointer);
var
  NewCode: TInstruction;
begin
  NewCode.Opcode := $E9;//jump relative
  NewCode.Offset := NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode);
  PatchCode(OldAddress, NewCode, SizeOf(NewCode));
end;

type
  TBitmapCanvas = class(TCanvas)
    // you need to implement this class
  end;

type
  TBitmapHelper = class helper for TBitmap
    function NewGetCanvas: TCanvas;
    class procedure Patch;
  end;

function TBitmapHelper.NewGetCanvas: TCanvas;
begin
  if Self.FCanvas = nil then
  begin
    Self.HandleNeeded;
    if Self.FCanvas = nil then
    begin
      Self.FCanvas := TBitmapCanvas.Create;
      Self.FCanvas.OnChange := Self.Changed;
      Self.FCanvas.OnChanging := Self.Changing;
    end;
  end;
  Result := Self.FCanvas;
end;

class procedure TBitmapHelper.Patch;
begin
  RedirectProcedure(@TBitmap.GetCanvas, @TBitmap.NewGetCanvas);
end;

initialization
  TBitmap.Patch;

end.

Include this unit in your project and the TBitmap class will be patched so that its GetCanvas method redirects to NewGetCanvas and allows you to implement your own TCanvas subclass. 在您的项目中包含此单元,将修补TBitmap类,以便其GetCanvas方法重定向到NewGetCanvas并允许您实现自己的TCanvas子类。

I don't think the code will work if you are using runtime packages but to sort that out you just need to use more capable hooking code. 我不认为如果您使用运行时包,代码将起作用,但要排序,您只需要使用更强大的挂钩代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM