[英]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. 这是由继承自
TCanvas
的Pixels
属性的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.