众所周知,在开箱即用的 VCL 中使用TBitmap
's 像素 ( ) 非常慢。Bitmap.Canvas.Pixels[X,Y]
这是由Pixels
继承自的属性的getter和setter引起的TCanvas
,它封装了通用的WinGDI DC对象,并不特定于位图的MemDC。
对于基于 DIB 部分的bmDIB
位图(在针对不同的 VCL 版本进行编译时会很严厉)。
请告知是否有一些骇人听闻的方式来访问TBitmapCanvas
类并将覆盖的方法注入其中。
我敢肯定它可以做得更优雅,但这是您要求使用类助手来破解私有成员的要求:
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
子类。
如果您使用运行时包,我认为代码不会起作用,但要解决这个问题,您只需要使用功能更强大的挂钩代码。