现在的位置: 首页 > 综合 > 正文

Delphi图像处理 — 平面几何变换类

2013年08月31日 ⁄ 综合 ⁄ 共 14844字 ⁄ 字号 评论关闭

阅读提示:

    《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。

    《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。

    尽可能保持二者内容一致,可相互对照。

    本文代码必须包括文章《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元

 

    有关图形图像的平面几何变换,现有的教程、计算机图书以及网上的资料上介绍理论的偏多,即使有些编程实例,也只是介绍图像几何变换的某些特例,如旋转、缩放、平移等。GDI+倒是有个Matrix类,可完整地实现图像的几何变换,可惜没法得到源码。

    本文不准备介绍任何关于平面几何变换的理论或者原理,而是直接用Delphi实现一个图形图像平面几何类TTransformMatrix。(C++版请见:http://blog.csdn.net/maozefa/archive/2010/10/10/5931427.aspx

    下面是TransformMatrix类的全部代码:

unit TransformMatrix;

(*****************************************************************************
*                                                                            *
* 本单元定义平面几何变换类                                                   *
*                                                                            *
* 编制人: 湖北省公安县统计局  毛泽发  2010.10                                *
*                                                                            *
*****************************************************************************)

interface

{$IF RTLVersion >= 17.00}
{$inline auto}
{$IFEND}

uses
  Windows, SysUtils, Gdiplus;

type
  // 几何变换矩阵结构
  PMatrixElements = Gdiplus.PMatrixElements;
  TMatrixElements = Gdiplus.TMatrixElements;

  // 平面几何变换类
  TTransformMatrix = class(TObject)
  private
    FElements: TMatrixElements;
    function GetIdentity: Boolean;
    function GetInvertible: Boolean;
    procedure SetElements(const Value: TMatrixElements);

    function GetIdentityElements: TMatrixElements;
    procedure ElementsMultiply(const e: TMatrixElements);
  public
    // 建立一个新实例,并初始化为单位矩阵 Elements = 1,0,0,1,0,0
    constructor Create; overload;
    // 建立一个新实例,并复制matrix的元素
    constructor Create(matrix: TTransformMatrix); overload;
    // 建立一个按指定的元素初始化的新实例
    constructor Create(m11, m12, m21, m22, dx, dy: Single); overload;
    // 重置对象为单位矩阵
    procedure Reset;
    // 将对象与matrix相乘
    procedure Multiply(const matrix: TTransformMatrix);
    // 设置平移
    procedure Translate(offsetX, offsetY: Single);
    // 设置缩放
    procedure Scale(scaleX, scaleY: Single);
    // 设置按角度angle沿原点旋转
    procedure Rotate(angle: Single);
    // 设置按角度angle沿中心点centerX, centerY旋转
    procedure RotateAt(angle: Single; centerX, centerY: Single);
    // 设置剪切,注意不要将shearX, shearY同时设置为1
    procedure Shear(shearX, shearY: Single);
    // 如果此对象是可逆转的,则逆转该对象。
    procedure Invert;
    // 按给定的大小计算并返回实施变换后的尺寸
    procedure GetTransformSize(width, height: Integer; var fx, fy, fwidth, fheight: Single);
    // 按给定的大小计算并返回实施变换后的尺寸
    function GetTransformRect(width, height: Integer): TRect;

    // 判断对象是否是可逆转的
    property IsInvertible: Boolean read GetInvertible;
    // 判断此对象是否是单位矩阵
    property IsIdentity: Boolean read GetIdentity;
    // 获取或设置对象元素
    property Elements: TMatrixElements read FElements write SetElements;
    // 获取对象的x偏移量
    property OffsetX: Single read FElements.dx write FElements.dx;
    // 获取对象的y偏移量
    property OffsetY: Single read FElements.dy write FElements.dy;
  end;

  // 设置双立方插值的斜率。缺省值为-0.75。返回设置前的值
  function SetBicubicSlope(const Value: Single): Single;

  // 临近插值过程。汇编调用
  procedure _GetNearColor;
  // 线性插值过程。汇编调用
  procedure _GetBilinearColor;
  // 双立方插值过程。汇编调用
  procedure _GetBicubicColor;

implementation

{ TTransformMatrix }

constructor TTransformMatrix.Create;
begin
  FElements.m11 := 1.0;
  FElements.m22 := 1.0;
end;

constructor TTransformMatrix.Create(matrix: TTransformMatrix);
begin
  FElements := matrix.Elements;
end;

constructor TTransformMatrix.Create(m11, m12, m21, m22, dx, dy: Single);
begin
  FElements.m11 := m11;
  FElements.m12 := m12;
  FElements.m21 := m21;
  FElements.m22 := m22;
  FElements.dx := dx;
  FElements.dy := dy;
end;

procedure TTransformMatrix.ElementsMultiply(const e: TMatrixElements);
var
  m11, m12: Single;
begin
  m11 := FElements.m11;
  m12 := FElements.m12;
  FElements.m11 := e.m11 * m11 + e.m12 * FElements.m21;
	FElements.m12 := e.m11 * m12 + e.m12 * FElements.m22;
	FElements.m21 := e.m21 * m11 + e.m22 * FElements.m21;
	FElements.m22 := e.m21 * m12 + e.m22 * FElements.m22;
end;

function TTransformMatrix.GetIdentity: Boolean;
begin
  Result := (FElements.m11 = 1.0) and (FElements.m12 = 0.0) and
            (FElements.m21 = 0.0) and (FElements.m22 = 1.0) and
            (FElements.dx = 0.0)  and (FElements.dy = 0.0);
end;

function TTransformMatrix.GetIdentityElements: TMatrixElements;
begin
  FillChar(Result, Sizeof(TMatrixElements), 0);
  Result.m11 := 1.0;
  Result.m22 := 1.0;
end;

function TTransformMatrix.GetInvertible: Boolean;
begin
  Result := Round((FElements.m11 * FElements.m22 - FElements.m12 * FElements.m21) * 1000.0) <> 0;
end;

function TTransformMatrix.GetTransformRect(width, height: Integer): TRect;
var
  fx, fy, fwidth, fheight: Single;
begin
  GetTransformSize(width, height, fx, fy, fwidth, fheight);
  Result.Left := Trunc(fx);
  Result.Top := Trunc(fy);
  Result.Right := Trunc(fwidth + fx + 0.999999);
  Result.Bottom := Trunc(fheight + fy + 0.999999);
end;

procedure TTransformMatrix.GetTransformSize(width, height: Integer; var fx, fy,
  fwidth, fheight: Single);
var
  fxs, fys: array[0..2] of Single;
  v: Single;
  i: Integer;
begin
  fxs[0] := width;
  fxs[1] := 0.0;
  fxs[2] := width;
  fys[0] := 0.0;
  fys[1] := height;
  fys[2] := height;
  fx := 0.0;
  fy := 0.0;
  fwidth := 0.0;
  fheight := 0.0;
  for i := 0 to 2 do
  begin
	  v := fxs[i] * FElements.m11 + fys[i] * FElements.m21;
  	if v < fx then fx := v
		else if v > fwidth then fwidth := v;
		v := fxs[i] * FElements.m12 + fys[i] * FElements.m22;
		if v < fy then fy := v
		else if v > fheight then fheight := v;
  end;
  fwidth := fwidth - fx;
	fheight := fheight - fy;
	fx := fx + FElements.dx;
	fy := fy + FElements.dy;
end;

(***************************************************************************
*  | m11' m12' |           1           | m22 -m12 |
*  |           | = ----------------- * |          |
*  | m21' m22' |   m11*m22 - m12*m21   |-m21  m11 |
*
*  dx' = dx * m11' - dy * m21'
*  dy' = dx * m12' - dy * m22'
***************************************************************************)
procedure TTransformMatrix.Invert;
var
  tmp: Double;
  m11, dx: Single;
begin
  tmp := FElements.m11 * FElements.m22 - FElements.m12 * FElements.m21;
  if Trunc(tmp * 1000.0) = 0 then
    raise Exception.Create('Not invertible transformation matrix.');
  tmp := 1.0 / tmp;
	m11 := FElements.m11;
	dx := -FElements.dx;
  FElements.m11 := tmp * FElements.m22;
	FElements.m12 := tmp * -FElements.m12;
	FElements.m21 := tmp * -FElements.m21;
	FElements.m22 := tmp * m11;
	FElements.dx := dx * FElements.m11 - FElements.dy * FElements.m21;
	FElements.dy := dx * FElements.m12 - FElements.dy * FElements.m22;
end;

procedure TTransformMatrix.Multiply(const matrix: TTransformMatrix);
begin
  FElements.dx := FElements.dx + (matrix.FElements.dx * FElements.m11 +
    matrix.FElements.dy * FElements.m21);
  FElements.dy := FElements.dy + (matrix.FElements.dx * FElements.m12 +
    matrix.FElements.dy * FElements.m22);
  ElementsMultiply(matrix.FElements);
end;

procedure TTransformMatrix.Reset;
begin
  FElements := GetIdentityElements;
end;

procedure TTransformMatrix.Rotate(angle: Single);
var
  e: TMatrixElements;
begin
  angle := angle * PI / 180.0;
  e.m11 := Cos(angle);
  e.m22 := e.m11;
  e.m12 := Sin(angle);
  e.m21 := -e.m12;
  e.dx := 0.0;
  e.dy := 0.0;
  ElementsMultiply(e);
end;

procedure TTransformMatrix.RotateAt(angle, centerX, centerY: Single);
begin
  Translate(centerX, centerY);
	Rotate(angle);
	Translate(-centerX, -centerY);
end;

procedure TTransformMatrix.Scale(scaleX, scaleY: Single);
var
  e: TMatrixElements;
begin
  e := GetIdentityElements;
  e.m11 := scaleX;
	e.m22 := scaleY;
	ElementsMultiply(e);
end;

procedure TTransformMatrix.SetElements(const Value: TMatrixElements);
begin
  Move(Value, FElements, Sizeof(TMatrixElements));
end;

procedure TTransformMatrix.Shear(shearX, shearY: Single);
var
  e: TMatrixElements;
begin
  e := GetIdentityElements;
  e.m21 := shearX;
	e.m12 := shearY;
	ElementsMultiply(e);
end;

procedure TTransformMatrix.Translate(offsetX, offsetY: Single);
begin
  FElements.dx := FElements.dx + (offsetX * FElements.m11 + offsetY * FElements.m21);
  FElements.dy := FElements.dy + (offsetX * FElements.m12 + offsetY * FElements.m22);
end;

{ 插值过程 }

type
  PXMMType = ^TXMMType;
  TXMMType = array[0..7] of Word;

var
  BilinearTab: PXMMType;              // emm 线形插值用表
  BicubicTab: PXMMType;               // emm 双立方插值用表

// <-->xmm7 8 * word=0
// <-->ebx stride
// --> edx x * 4096
// --> ecx y * 4096
// <-->esi = data->Scan0 + y  * data->Stride + x * 4
// <-- xmm0 BilinearColor
procedure _GetBilinearColor;
asm
    push        ecx
    push        edx
    and         edx, 0ff0h
    and         ecx, 0ff0h
    mov         eax, BilinearTab
    movq        xmm0, [esi]
    movq        xmm1, [esi+ebx]
    punpcklbw   xmm0, xmm7            // xmm0 = pixel2, pixel0
    punpcklbw   xmm1, xmm7            // xmm1 = pixel3, pixel1
    pmulhuw     xmm0, [eax+ecx]       // xmm0 *= vr
    pmulhuw     xmm1, [eax+ecx+256*16]// xmm1 *= v
    paddw       xmm0, xmm1
    pmulhuw     xmm0, [eax+edx+512*16]// xmm0 *= u, ur
    movdqa      xmm1, xmm0
    psrldq      xmm0, 8               // xmm0 >>= 64
    paddw       xmm0, xmm1
    packuswb    xmm0, xmm7
    pop         edx
    pop         ecx
end;

// <--> xmm7 8 * word = 0
// <--> xmm6 8 * word = 4
// <--> ebx stride
// -->  edx x * 4096
// -->  ecx y * 4096
// <--> esi = data->Scan0 + y  * data->Stride + x * 4
// <--  xmm0 BicubicColor
procedure _GetBicubicColor;
asm
    push        edi
    push        ecx
    push        edx
    mov         edi, BicubicTab
    and         edx, 0ff0h        // u = x & 4095
    and         ecx, 0ff0h        // v = y & 4096
    mov         eax, edx          // eax = -u
    neg         eax
    movq        xmm1, [esi]
    movq        xmm2, [esi+8]
    movq        xmm3, [esi+ebx]
    movq        xmm4, [esi+ebx+8]
    punpcklbw   xmm1, xmm7
    punpcklbw   xmm2, xmm7
    punpcklbw   xmm3, xmm7
    punpcklbw   xmm4, xmm7
    psllw       xmm1, 7
    psllw       xmm2, 7
    psllw       xmm3, 7
    psllw       xmm4, 7
    pmulhw      xmm1, [edi+edx]
    pmulhw      xmm2, [edi+eax+512*16]
    pmulhw      xmm3, [edi+edx]
    pmulhw      xmm4, [edi+eax+512*16]
    paddsw      xmm1, xmm2
    paddsw      xmm3, xmm4
    movdqa      xmm0, xmm1
    movdqa      xmm4, xmm3
    psrldq      xmm0, 8
    psrldq      xmm3, 8
    paddsw      xmm0, xmm1
    paddsw      xmm3, xmm4
    punpcklqdq  xmm0, xmm3
    pmulhw      xmm0, [edi+ecx]
    neg         ecx
    add         esi, ebx
    movq        xmm1, [esi+ebx]
    movq        xmm2, [esi+ebx+8]
    movq        xmm3, [esi+ebx*2]
    movq        xmm4, [esi+ebx*2+8]
    punpcklbw   xmm1, xmm7
    punpcklbw   xmm2, xmm7
    punpcklbw   xmm3, xmm7
    punpcklbw   xmm4, xmm7
    psllw       xmm1, 7
    psllw       xmm2, 7
    psllw       xmm3, 7
    psllw       xmm4, 7
    pmulhw      xmm1, [edi+edx]
    pmulhw      xmm2, [edi+eax+512*16]
    pmulhw      xmm3, [edi+edx]
    pmulhw      xmm4, [edi+eax+512*16]
    paddsw      xmm1, xmm2
    paddsw      xmm3, xmm4
    movdqa      xmm2, xmm1
    movdqa      xmm4, xmm3
    psrldq      xmm1, 8
    psrldq      xmm3, 8
    paddsw      xmm1, xmm2
    paddsw      xmm3, xmm4
    punpcklqdq  xmm1, xmm3
    pmulhw      xmm1, [edi+ecx+512*16]
    paddsw      xmm0, xmm1
    movdqa      xmm1, xmm0
    psrldq      xmm0, 8
    paddsw      xmm0, xmm1
    paddsw      xmm0, xmm6
    psraw       xmm0, 3
    packuswb    xmm0, xmm7
    pop         edx
    pop         ecx
    pop         edi
end;

procedure _GetNearColor;
asm
    movd      xmm0, [esi]
end;

var
  PMMXTable: Pointer;
  BicubicSlope: Single = 0;

function SetBicubicSlope(const Value: Single): Single;

  function BicubicFunc(x : double): double;
  var
    x2, x3: double;
  begin
    if x < 0.0 then x := -x;
    x2 := x * x;
    x3 := x2 * x;
    if x <= 1.0 then
      Result := (Value + 2.0) * x3 - (Value + 3.0) * x2 + 1.0
    else if x <= 2.0 then
      Result := Value * x3 - (5.0 * Value) * x2 + (8.0 * Value) * x - (4.0 * Value)
    else Result := 0.0;
  end;

var
  I: Integer;
  P: PXMMType;
begin
  Result := BicubicSlope;
  if (BicubicSlope <> Value) and (Value < 0.0) {and (Value >= -2.0)} then
  begin
    BicubicSlope := Value;
    P := BicubicTab;
    for I := 0 to 255 do
    begin
      p^[0] := Round(16384 * BicubicFunc((I + 256) * (1.0 / 256)));
      p^[1] := p^[0];
      p^[2] := p^[0];
      p^[3] := p^[0];
      p^[4] := Round(16384 * BicubicFunc(I * (1.0 / 256)));
      p^[5] := p^[4];
      p^[6] := p^[4];
      p^[7] := p^[4];
      Inc(p);
    end;
    for I := 256 to 512 do
    begin
      p^[0] := Round(16384 * BicubicFunc((I - 256) * (1.0 / 256)));
      p^[1] := p^[0];
      p^[2] := p^[0];
      p^[3] := p^[0];
      p^[4] := Round(16384 * BicubicFunc(I * (1.0 / 256)));
      p^[5] := p^[4];
      p^[6] := p^[4];
      p^[7] := p^[4];
      Inc(p);
    end;
  end;
end;

procedure InitBilinearTab;
asm
    mov     edx, BilinearTab
    mov     eax, 1000100h
    movd    xmm7, eax
    pshufd  xmm7, xmm7, 0
    mov     eax, 10001h
    movd    xmm6, eax
    pshufd  xmm6, xmm6, 0
    mov     eax, 0ff00ffh
    movd    xmm0, eax
    pshufd  xmm0, xmm0, 0
    movdqa  xmm1, xmm6
    xor     ecx, ecx
    jmp     @@1
@@sumLoop:
    mov     eax, ecx
    shl     eax, 16
    or      eax, ecx
    movd    xmm1, eax
    pshufd  xmm1, xmm1, 0
    movdqa  xmm0, xmm7
    psubw   xmm0, xmm1
    cmp     ecx, 255
    je      @@1
    paddw   xmm1, xmm6
@@1:
    psllw   xmm0, 8
    psllw   xmm1, 8
    movdqa  xmm2, xmm0
    movlhps xmm2, xmm1
    movdqa  [edx], xmm0         // vr
    movdqa  [edx+256*16], xmm1  // v
    movdqa  [edx+512*16], xmm2  // high 64 u, lower 64 ur
    add     edx, 16
    inc     ecx
    cmp     ecx, 256
    jb      @@sumLoop
end;

const
  BilinearTabSize = 256 * 3 * 16;
  BicubicTabSize = 513 * 16;

initialization
begin
  GetMem(PMMXTable, BilinearTabSize + BicubicTabSize + 12);
  BilinearTab := PXMMType((Integer(PMMXTable) + 12) and (not 15));
  BicubicTab := PXMMType(Integer(BilinearTab) + BilinearTabSize);
  InitBilinearTab;
  SetBicubicSlope(-0.75);
end;
finalization
begin
  FreeMem(PMMXTable);
end;

end.

    TTransformMatrix与GDI+的TGpMatrix布局基本一样,所以关于类的使用方法就不再介绍了,本文的目的在于如何实现自己的平面几何变换类,否则,不如直接用GDI+的TGpMatrix了。

    TTransformMatrix的核心代码是Multiply方法(或ElementsMultiply方法)和Invert方法。

    Multiply方法通过2个TTransformMatrix的相乘来实现各种复杂的几何变换计算,所有能够实现的具体几何变换都是可以通过其完成的(代码中的平移函数Translate也可以通过其完成的,当然多了一些不必要的计算)。无论是TTransformMatrix类还是GDI+的TGpMatrix类,所提供的都只是基本的几何变换方法,还有些图形图像几何变换,如对称几何变换(镜像)和各种复杂的组合变换。都只能通过Multiply方法或者更直接的变换矩阵成员设置去实现。

    Invert方法实现了变换矩阵的逆矩阵,通过这个几何变换逆矩阵,可以很方便地实现图形图像几何变换的实际操作。为什么要靠几何变换矩阵的逆矩阵,而不是直接依据变换矩阵来实现图形图像几何变换的实际操作呢?因为几何变换矩阵表示的意思是,把源图像的任意座标点通过几何变换后投影到目标图像。而源图像像素通过几何变换后与目标图像上的像素点有可能不能一一对应,如图像缩放变换后,不是多个源图像像素点对应同一个目标像素点(缩小),就是源图像像素点不足以填充全部的目标像素点(放大),这就有可能造成目标图像像素点被重复绘制或者被遗漏的现象发生;而几何变换逆矩阵所表示的意思是,对于目标图像任意一个像素点,如果在几何变换前有源图像像素点与其对应,则进行复制。遍历目标图像像素点就能保证目标图像像素点既不重复、也不遗漏的被复制。

    上面TransformMatrix.pas单元还提供了几个插值过程,供具体的图像几何变换过程使用,限于篇幅,本文只介绍TTransformMatrix,没有用到这几个过程,也就没作介绍,放在这里,只是为了方便,因为具体的图像几何变换过程必须包括TransformMatrix.pas单元,这样就不会找不到这几个过程了。当然,为了检验TTransformMatrix类,还是写了个简单的过程来实现图像几何变换,下面是这个过程的代码: 

procedure Transform(var dest: TImageData; x, y: Integer;
  const source: TImageData; matrix: TTransformMatrix);
var
  m: TTransformMatrix;
  e: TMatrixElements;
  fx, fy, fwidth, fheight: Single;
  x0, y0, dstOffset: Integer;
  xs, ys, xs0, ys0: Single;
  pix: PARGB;
  dst: TImageData;
begin
  // 复制几何变换矩阵对象
  m := TTransformMatrix.Create(matrix);
  try
    // 几何变换矩阵绝对增加平移量x, y
    m.OffsetX := m.OffsetX + x;
    m.OffsetY := m.OffsetY + y;
    // 按几何变换矩阵计算并获取目标图像数据子数据
    m.GetTransformSize(source.Width, source.Height, fx, fy, fwidth, fheight);
    dst := GetSubImageData(dest, Trunc(fx), Trunc(fy), Trunc(fwidth + 0.999999), Trunc(fheight + 0.999999));
    if dst.Scan0 = nil then Exit;
    // 获取几何变换逆矩阵
    m.Invert;
    // 如果子图数据与目标图像原点不一致,几何变换矩阵相对增加平移量fx, fy
    if (fx > 0.0)  or (fy > 0.0) then
    begin
      if fx < 0.0 then fx := 0.0
      else if fy < 0.0 then fy := 0.0;
      m.Translate(fx, fy);
    end;
    // 设置子图扫描线指针及行偏移宽度
    pix := dst.Scan0;
    dstOffset := dst.Stride div 4 - Integer(dst.Width);
    // 几何变换逆矩阵的平移量为与子图原点对应的源图起始坐标点
    e := m.Elements;
    xs := e.dx;
    ys := e.dy;
    // 逐点计算并复制源图几何变换后的数据到目标子图
    for y := 1 to dst.Height do
    begin
      xs0 := xs;
      ys0 := ys;
      for x := 1 to dst.Width do
      begin
        x0 := Round(xs0);
        y0 := Round(ys0);
        if (x0 >= 0) and (x0 < Integer(source.Width)) and
           (y0 >= 0) and (y0 < Integer(source.Height)) then
          pix^ := PARGB(Integer(source.Scan0) + y0 * source.Stride + x0 shl 2)^;
        Inc(pix);
        xs0 := xs0 + e.m11;
        ys0 := ys0 + e.m12;
      end;
      Inc(pix, dstOffset);
      xs := xs + e.m21;
      ys := ys + e.m22;
    end;
  finally
    m.Free;
  end;
end;

    GetSubBitmapData函数(《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元)用于获取一个界定了范围的子图像数据,减少了像素操作时的计算,而Transform过程则用来实现具体的图像几何变换。上面之所以说“简单”,指的是Transform过程复制像素时使用的是直接临近取值,这样转换出来的图像质量较差;而且计算像素地址时采用了浮点数运算,影响了变换速度。但是这个过程的框架却是较完整的,可在此基础上加入像素插值方式,再改浮点数运算为定点数运算,该过程就比较完善了。

    下面是分别使用Delpha的TBitmap类型和GDI+的TGpBitmap类型进行图像缩放加剪切的例子:

procedure TForm1.Button1Click(Sender: TObject);
var
  bmp, newBmp: TBitmap;
  jpg: TJPEGImage;
  matrix: TTransformMatrix;
  source, dest, exp: TImageData;
  r: TRect;
begin
  bmp := TBitmap.Create;
  matrix := TTransformMatrix.Create;
  try
    jpg := TJPEGImage.Create;
    try
      jpg.LoadFromFile('..\..\media\IMG_9440_mf.jpg');
      bmp.Assign(jpg);
    finally
      jpg.Free;
    end;
    matrix.Scale(1.2, 1.2);
    matrix.Shear(0.5, 0.5);
    r := matrix.GetTransformRect(bmp.Width, bmp.Height);
    if (r.Right <= 0) or (r.Bottom <= 0) then Exit;
    source := GetBitmapData(bmp);
    newBmp := TBitmap.Create;
    try
      newBmp.PixelFormat := pf32bit;
      newBmp.Width := r.Right;
      newBmp.Height := r.Bottom;
      dest := GetBitmapData(newBmp);
      Transform(dest, 0, 0, source, matrix);
      Canvas.Draw(0, 0, newBmp);
    finally
      newBmp.Free;
    end;
  finally
    matrix.Free;
    bmp.Free;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  bmp, newBmp: TGpBitmap;
  matrix: TTransformMatrix;
  source, dest: TImageData;
  g: TGpGraphics;
  r: TRect;
begin
  bmp := TGpBitmap.Create('..\..\media\IMG_9440_mf.jpg');
  matrix := TTransformMatrix.Create;
  try
    matrix.Scale(1.2, 1.2);
    matrix.Shear(0.5, 0.5);
    r := matrix.GetTransformRect(bmp.Width, bmp.Height);
    if (r.Right <= 0) or (r.Bottom <= 0) then Exit;
    newBmp := TGpBitmap.Create(r.Right, r.Bottom, pf32bppARGB);
    g := TGpGraphics.Create(Canvas.Handle);
    try
      source := LockGpBitmap(bmp);
      dest :=  LockGpBitmap(newBmp);
      Transform(dest, 0, 0, source, matrix);
      UnlockGpBitmap(newBmp, dest);
      UnlockGpBitmap(bmp, source);
      g.DrawImage(newBmp, 0, 0);
    finally
      g.Free;
      newBmp.Free;
    end;
  finally
    matrix.Free;
    bmp.Free;
  end;
end;

     2个例子运行界面类似。下面是原图像和运行界面截图:

    说明:本文中使用的GDI+版本下载地址和说明请见《GDI+ for VCL基础 -- GDI+ 与 VCL》。

    如果使用与本文不同的GDI+版本,或者不使用GDI+,可将前面TransformMatrix单元里的:

    // 几何变换矩阵结构
    PMatrixElements = Gdiplus.PMatrixElements;
    TMatrixElements = Gdiplus.TMatrixElements;

    改为:

  TMatrixElements = packed record
    
case Integer of
      
0: (Elements: array[0..5of  Single);
      
1: (m11, m12, m21, m22, dx, dy: Single);
  
end;
  PMatrixElements 
= ^TMatrixElements;

 

    《Delphi图像处理》系列使用GDI+单元下载地址和说明见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。

    因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

    这里可访问《Delphi图像处理 -- 文章索引》。

 

抱歉!评论已关闭.