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

时间:2013-05-13 08:08:20

标签: delphi vcl

众所周知,在开箱即用的VCL中使用TBitmap的像素(Bitmap.Canvas.Pixels[X,Y])非常慢。这是由继承自Pixels的{​​{1}}属性的getter和setter引起的,该属性封装了一般的WinGDI DC对象,并不特定于位图的MemDC。

对于基于DIB部分的位图(TCanvas),存在well-known workaround,但是我没有看到在VCL TBitmap类中集成正确的getter / setter的方法(除了直接修改库之外)在针对不同的VCL版本进行编译时,代码被证明是严厉的痛苦

请告知是否有某种破解方法可以到达bmDIB课程并将重写方法注入其中。

1 个答案:

答案 0 :(得分:4)

我确信它可以更优雅地完成,但是这就是你要求使用类助手来破解私人成员的要求:

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.

在您的项目中包含此单元,TBitmap类将进行修补,以便其GetCanvas方法重定向到NewGetCanvas,并允许您实现自己的TCanvas子类。

如果您使用的是运行时包,我认为代码不会起作用,但要对其进行排序,您只需要使用更强大的挂钩代码。