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

来源:互联网 发布:安装驱动程序软件错误 编辑:程序博客网 时间:2024/05/22 07:06

阅读提示:

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

 

原创粉丝点击