阅读提示:
《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。
《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。
尽可能保持二者内容一致,可相互对照。
本文代码必须包括文章《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元和《Delphi图像处理 -- 平面几何变换类》TransformMatrix.pas单元,另外,文章中还调用了《Delphi图像处理 -- 图像合成》_DoMixer过程(如嫌麻烦,可把调用的代码删除,不影响本文代码的完整)。
《Delphi图像处理 -- 几何变换(上)》中的ImageTransform过程能够处理所有的图像平面几何变换,而且效果和速度也还不错,但是我们还是有必要作进一步的改善,改善的要点就是将几何变换中缩放功能独立出来,因为无论是图像处理,还是窗口界面作图,使用缩放的频率是很高的,独立后可大大加快缩放的效率。
下面是图像缩放部分的代码,所调用的其它过程同ImageTransform:
procedure _DoScale(var Dest: TImageData; const Soutce: TImageData;
Radius, xDelta, yDelta: Integer; ipProc: TInterpolateProc);
var
width, height, dstOffset: Integer;
scan0: Pointer;
asm
push esi
push edi
push ebx
mov ebx, [eax].TImageData.Height
mov height, ebx
mov ebx, [eax].TImageData.Width
mov width, ebx
shl ebx, 2
neg ebx
add ebx, [eax].TImageData.Stride
mov dstOffset, ebx
mov edi, [eax].TImageData.Scan0
mov esi, [edx].TImageData.Scan0
mov scan0, esi
mov al, [edx].TImageData.AlphaFlag
mov ebx, [edx].TImageData.Stride
pxor xmm7, xmm7
mov edx, 40004h
movd xmm6, edx
pshufd xmm6, xmm6, 0
shl ecx, 11
add ecx, 800h
mov edx, ecx
cmp al, True
je @@ArgbMix
cmp [esi].TARGBQuad.Alpha, 255
jne @@ArgbMix
@@yLoop:
mov esi, ecx // for (; y != Height; y += yDelta)
sar esi, 12 // {
imul esi, ebx
add esi, Scan0 // esi = src.Scan0 + y / 4096 * src.Stride
push width
push edx
@@xLoop:
push esi // for (; x != Width; x += xDelta)
mov eax, edx // {
sar eax, 12
shl eax, 2
add esi, eax // esi += (x / 4096 * 4)
call ipProc // xmm0 = GetColor(src, esi, x, y)
movd [edi], xmm0 // *(ARGB*)edi = xmm0
pop esi
add edi, 4 // edi += 4
add edx, xDelta
dec width
jnz @@xLoop // }
pop edx
pop width
add edi, dstOffset // edi += dstOffset
add ecx, yDelta
dec height
jnz @@yLoop // }
jmp @@End
@@ArgbMix:
call SetMixerMM
@@yLoopA:
mov esi, ecx
sar esi, 12
imul esi, ebx
add esi, Scan0
push width
push edx
@@xLoopA:
push esi
mov eax, edx
sar eax, 12
shl eax, 2
add esi, eax
call ipProc
movd eax, xmm0
shr eax, 24
jz @@NextA
call MixerColor
@@NextA:
pop esi
add edi, 4
add edx, xDelta
dec width
jnz @@xLoopA
pop edx
pop width
add edi, dstOffset
add ecx, yDelta
dec height
jnz @@yLoopA
@@End:
emms
@@Exit:
pop ebx
pop edi
pop esi
end;
procedure ImageStretchMixer(var Dest: TImageData; const Source: TImageData; Alpha: Single = 1);
var
alphaI, radius: Integer;
proc: TInterpolateProc;
src, sub: TImageData;
begin
alphaI := Round(Alpha * 256);
if alphaI <= 0 then Exit;
if alphaI > 256 then alphaI := 256;
if (Dest.Width = Source.Width) and (Dest.Height = Source.Height) then
begin
_DoMixer(Dest, Source, alphaI);
Exit;
end;
radius := GetInterpolateProc(Source, proc);
src := NewImageData(Source.Width + radius * 2, Source.Height + radius * 2);
try
sub := GetSubImageData(src, radius, radius, Source.Width, Source.Height);
CopyInterpolateData(sub, Source, alphaI);
src.AlphaFlag := sub.AlphaFlag;
FillBorder(src, radius, 0);
_DoScale(Dest, src, radius,
Round(Source.Width / Dest.Width * 4096),
Round(Source.Height / Dest.Height * 4096), proc);
if (alphaI = 256) and not Source.AlphaFlag then
Dest.AlphaFlag := False;
finally
FreeImageData(src);
end;
end;
缩放的核心代码是_DoScale过程,这个过程比ImageTransform过程多了一段拷贝缩放代码,专门处理没有Alpha信息的图像,ImageTransform过程因为要处理边界(无论图像是否含Alpha信息,边界总是按Alpha混合的),所以不能采用拷贝形式。另外在图像x,y方向缩放都为1时,直接调用了_DoMixer过程处理(见《Delphi图像处理 -- 图像合成》,如果嫌麻烦,也可把这段代码删掉)。ImageStretchMixer是一个调用_DoDcale作拉伸合成的过程,缩放以及所有几何变换也可看作是图像合成。
缩放功能独立出来后,ImageTransform过程也要作相应修改:
procedure ImageTransform(var Dest: TImageData; x, y: Integer;
const Source: TImageData; const Matrix: TTransformMatrix; Alpha: Single = 1);
var
m: TTransformMatrix;
e: TMatrixElements;
eI: TElementsI;
dstR, srcR: TRect;
i, alphaI, radius: Integer;
proc: TInterpolateProc;
dst, src, tmp, sub: TImageData;
begin
alphaI := Round(Alpha * 256);
if alphaI <= 0 then Exit;
if alphaI > 256 then alphaI := 256;
m := TTransformMatrix.Create(Matrix);
try
m.OffsetX := m.OffsetX + x;
m.OffsetY := m.OffsetY + y;
if not _GetTransformParams(Dest.Width, Dest.Height,
Source.Width, Source.Height, m, dstR, srcR) then Exit;
e := m.Elements;
for i := 0 to 5 do
eI[i] := Round(e.Elements[i] * 4096);
radius := GetInterpolateProc(Source, proc);
dst := GetSubImageData(Dest, dstR.Left, dstR.Top, dstR.Right, dstR.Bottom);
tmp := GetSubImageData(Source, srcR.Left, srcR.Top, srcR.Right, srcR.Bottom);
if (eI[0] = $1000) and (eI[3] = $1000) and (eI[1] + eI[2] = 0) then
_DoMixer(dst, tmp, alphaI)
else
begin
src := NewImageData(tmp.Width + radius * 2, tmp.Height + radius * 2);
try
src.AlphaFlag := Source.AlphaFlag;
sub := GetSubImageData(src, radius, radius, tmp.Width, tmp.Height);
CopyInterpolateData(sub, tmp, alphaI);
FillBorder(src, radius, (eI[1] shl 16) or (eI[2] and $ffff));
if (eI[1] = 0) and (eI[2] = 0) then
begin
_DoScale(dst, src, radius, eI[0], eI[3], proc);
if (alphaI = 256) and not Source.AlphaFlag
and (Dest.Width = dst.Width) and (Dest.Height = dst.Height) then
Dest.AlphaFlag := False;
end
else
_DoTransform(dst, src, eI, radius, proc);
finally
FreeImageData(src);
end;
end;
finally
m.Free;
end;
end;
修改后的ImageTransform过程能自动根据几何变换条件作出判断使用哪个过程。
《Delphi图像处理》系列使用GDI+单元下载地址和说明见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。
因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com
这里可访问《Delphi图像处理 -- 文章索引》。