您的位置:首页 > 编程语言 > Delphi

GDI+ 在Delphi程序的应用 -- Matrix应用心得(转)

2007-09-27 10:06 597 查看
关于GDI+的几何变换类Matrix的理论和应用有很多书籍和文章介绍,本文只是谈一点自己的应用心得。
使用GDI+的Matrix类,可以很方便的进行提供了Rotate(旋转)、Scale( 缩放)、Shear(切变)等线性变换和Translate(平移),还可以通过这几个基本的变换组成更复杂的复合变换。通过变换后的图形及原点坐标变化很大,如下面的语句:

g.DrawEllipse(Pens.Blue, 0, 0, 100, 50);

g.ScaleTransform(1.0, 0.5);

g.TranslateTransform(50.0, 0.0, moAppend);

g.RotateTransform(30, moAppend);

g.DrawEllipse(Pens.Blue, 0, 0, 100, 50);

将产生下面的图像:

function CalcTransRect(Origin: TGpRectF; e: TMatrixElements): TGpRectF;

function GetTransPoint(x, y: Single): TGpPointF;

begin

Result.X := e.m11 * x + e.m21 * y;

Result.Y := e.m12 * x + e.m22 * y;

end;

var

R: TGpRectF;

pf: array[0..3] of TGpPointF;

I: Integer;

begin

R := Origin;

// 分别计算四个角的相对坐标

pf[0] := GetTransPoint(R.X, R.Y);

pf[1] := GetTransPoint(R.Width + R.X, R.Y);

pf[2] := GetTransPoint(R.X, R.Height + R.Y);

pf[3] := GetTransPoint(R.Width + R.X, R.Height + R.Y);

// 取得左上角和右下角的坐标点

R := GpRect(pf[0].X, pf[0].Y, 0.0, 0.0);

for I := 0 to 3 do

begin

if R.X > pf[I].X then R.X := pf[I].X

else if R.Width < pf[I].X then R.Width := pf[I].X;

if R.Y > pf[I].Y then R.Y := pf[I].Y

else if R.Height < pf[I].Y then R.Height := pf[I].Y;

end;

// 求出矩形尺寸

R.Width := R.Width - R.X;

R.Height := R.Height - R.Y;

// 求得左上角实际坐标

R.X := R.X + e.dx;

R.Y := R.Y + e.dy;

Result := R;

end;

procedure TMainForm.Button1Click(Sender: TObject);

var

Image: TGpImage;

g: TGpGraphics;

m: TGpMatrix;

r: TGpRectF;

begin

Image := TGpImage.Create('....Mediamsn.jpg');

g := TGpGraphics.Create(Handle, False);

m := TGpMatrix.Create;

try

r := GpRect(20.0, 20.0, Image.Width, Image.Height);

g.Clear(ARGBFromTColor(Color));

g.DrawImage(Image, r);

m.Rotate(30);

m.Scale(1.8, 0.8);

m.Shear(0.3, 0.2);

g.SetTransform(m);

g.DrawImage(Image, r);

r := CalcTransRect(r, m.Elements);

g.ResetTransform;

g.DrawRectangle(Pens.Red, r);

finally

Image.Free;

g.Free;

m.Free;

end;

end;

运行结果如下图,红色的矩形是计算出的变换后的坐标,与变换后的图像完全吻合:

g.DrawImage(Image, 20, 20, Image.Width, Image.Height); //用绝对坐标画原图

m.Translate(20, 20); // 坐标转换

m.Rotate(30);

m.Scale(1.8, 0.8);

m.Shear(0.3, 0.2);

g.SetTransform(m);

g.DrawImage(Image, 0, 0, Image.Width, Image.Height); //用相对坐标画图

其结果为下图,完全符合要求:

procedure ScaleAt(m: TGpMatrix; ScaleX, ScaleY, x, y: Single; Order: TMatrixOrder = moPrepend);

begin

if order = moAppend then

begin

x := -x;

y := -y;

end;

m.Translate(x, y, Order);

m.Scale(ScaleX, ScaleY, Order);

m.Translate(-x, -y, Order);

end;

procedure ShearAt(m: TGpMatrix; ShearX, ShearY, x, y: Single; Order: TMatrixOrder = moPrepend);

begin

if order = moAppend then

begin

x := -x;

y := -y;

end;

m.Translate(x, y, Order);

m.Shear(ShearX, ShearY, Order);

m.Translate(-x, -y, Order);

end;

procedure TMainForm.Button2Click(Sender: TObject);

var

Image: TGpImage;

g: TGpGraphics;

m: TGpMatrix;

begin

Image := TGpImage.Create('....Mediamsn.jpg');

g := TGpGraphics.Create(Handle, False);

m := TGpMatrix.Create;

try

g.DrawImage(Image, 20, 20, Image.Width, Image.Height);

m.RotateAt(30, GpPoint(20.0, 20.0), moAppend);

ScaleAt(m, 1.8, 0.8, 20, 20, moAppend);

ShearAt(m, 0.3, 0.2, 20, 20, moAppend);

g.SetTransform(m);

g.DrawImage(Image, 20, 20, Image.Width, Image.Height);

finally

Image.Free;

g.Free;

m.Free;

end;

end;

运行结果如下图,如果改为默认操作顺序,则和前面平移坐标后的运行结果完全一样:



说明,本文所用GDI+代码与网上流通的不兼容,如上面例子中的TMatrixElements,定义为:

TMatrixElements = packed record

case Integer of

0: (Elements: array[0..5] of Single);

1: (m11, m12, m21, m22, dx, dy: Single);

end;

而网上流通的则定义为一个数组:

TMatrixArray = array[0..5] of Single;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: