阅读提示:
《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》。
因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:[email protected]
这里可访问《Delphi图像处理 -- 文章索引》。