Delphi图像处理 -- 平面几何变换(下)

阅读提示:

    《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图像处理 -- 文章索引》。

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值