一:水平镜像/翻转
将一幅图像水平镜像/翻转,代码很简单,就一行代码:
procedure HorizMirror(bmp: TBitmap);
begin
bmp.Canvas.CopyRect(bmp.Canvas.ClipRect, bmp.Canvas, Rect(bmp.Width, 0, 0, bmp.Height));
end;
简单不。看一看 CopyRect 源码,
procedure TCanvas.CopyRect(const Dest: TRect; Canvas: TCanvas; const Source: TRect);
begin
Changing;
RequiredState([csHandleValid, csFontValid, csBrushValid]);
Canvas.RequiredState([csHandleValid, csBrushValid]);
StretchBlt(FHandle, Dest.Left, Dest.Top, Dest.Right - Dest.Left,
Dest.Bottom - Dest.Top, Canvas.FHandle, Source.Left, Source.Top,
Source.Right - Source.Left, Source.Bottom - Source.Top, CopyMode);
Changed;
end;
Changing / RequireState / Changed 肯定影响效率。真正起作用的是 StrectchBlt 函数。
虽然说这个 StrectchBlt 效率已经不错了。4096x4096x32 的图片,在我的机器上耗时 100 毫秒左右。
有没有可以在优化的地方呢,如果我们自己来写该如何呢?
自己写当然是用 Scanline 方法了(简单)。按中间位置,左右水平像素对调,再加上多核并行:
{ 水平翻转 并行模式,需要脱离 IDE 执行 }
procedure HorizMirror(bmp: TBitmap);
var
StartScanLine: Integer;
bmpWidthBytes: Integer;
begin
StartScanLine := Integer(bmp.ScanLine[0]);
bmpWidthBytes := Integer(bmp.ScanLine[1]) - Integer(bmp.ScanLine[0]);
TParallel.For(0, bmp.Height - 1,
procedure(Y: Integer)
var
X: Integer;
swapColor: DWORD;
pColor01: PDWORD;
pColor02: PDWORD;
begin
pColor01 := PDWORD(StartScanLine + Y * bmpWidthBytes);
pColor02 := PDWORD(StartScanLine + Y * bmpWidthBytes);
Inc(pColor02, bmp.Width - 1);
for X := 0 to bmp.Width div 2 - 1 do
begin
swapColor := pColor02^;
pColor02^ := pColor01^;
pColor01^ := swapColor;
Inc(pColor01);
Dec(pColor02);
end;
end);
end;
这段代码,在我的机器上,4096X4096X32 的图片,只需要 5 毫秒左右的时间,非常的理想了,无需进行 SSE 优化了。
二:垂直镜像/翻转
垂直镜像/翻转,同样也只需要一行代码:
{ 垂直翻转 }
procedure VertiMirror(bmp: TBitmap);
begin
bmp.Canvas.CopyRect(bmp.Canvas.ClipRect, bmp.Canvas, Rect(0, bmp.Height, bmp.Width, 0));
end;
在我的机器上,4096X4096X32 的图片,耗时 100 毫秒左右。
想一想,垂直镜像,就是上下行对调。整行内存交换呀。自己写的话,效率应该比 StrectchBlt 好。来试试:
{ 垂直镜像/翻转 }
procedure VertiMirror(bmp: TBitmap);
var
Count, Y: Integer;
pColor01: PByte;
pColor02: PByte;
tmpColor: PByte;
begin
Count := Integer(bmp.ScanLine[0]) - Integer(bmp.ScanLine[1]);
tmpColor := AllocMem(Count);
try
for Y := 0 to bmp.Height div 2 - 1 do
begin
pColor01 := bmp.ScanLine[Y];
pColor02 := bmp.ScanLine[bmp.Height - Y - 1];
Move(pColor01^, tmpColor^, Count);
Move(pColor02^, pColor01^, Count);
Move(tmpColor^, pColor02^, Count);
end;
finally
FreeMem(tmpColor);
end;
end;
这段代码,没有使用多核并行,也没有使用 SSE 优化。就是内存的交换。
在我的机器上耗时 15 毫秒左右。效率提高很明显呀。6倍之多!
印证了那句话:相信指令,相信编译器,不如相信自己。自己动手,丰衣足食!
当看到 AllocMem、Move,你想到了什么?
内存管理。Delphi 优化内存的有:FastMM4、FastMM5、FastMM4-AVX、FastCode、FastMove。
对。引入试试。
在工程单元文件中,第一行,添加引用:FastMove。函数不用做任何修改。再编译一下程序。
运行看看效果。在我的机器上耗时降到了 10 毫秒左右。呵呵。不错不错。
少了 5 毫秒的时间。如何少的,看看 FastMove 源码就知道了。留作课后作业了。
三:转置
即水平翻转+垂直翻转。
{ 转置翻转 并行模式,需要脱离 IDE 执行 }
procedure HAndVMirror(bmp: TBitmap);
begin
HorizMirror(bmp);
VertiMirror(bmp);
end;
详细代码:https://github.com/dbyoung720/ImageGray.git
qq交流群:101611228