• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

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

原作者: [db:作者] 来自: [db:来源] 收藏 邀请
    关于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);

将产生下面的图像:

但是,我们有时需要知道变换后的图形尺寸及坐标,如果通过编程计算求得,可能很麻烦,特别是一些复杂的组合变换。利用Matrix提供的变换后的矩阵数据,可以很简单的得到变换后的图形矩形。

        Matrix提供的是一个3 X 2 仿射矩阵,上面的2 X 2 矩阵是Rotate、Scale和Shear等线性变换后的数据,分别用m11,m21,m12,m22表示,可以用x = m11 * xi+ m21 * yi,y = m21 * xi + m22 * yi求得线性变换后图形的任意一点的相对坐标,我们只要计算四个角的坐标就行了;下面一行则是平移数据,用dx,dy表示,前面计算出的坐标点分别加上dx和dy就是实际的坐标点。下面是笔者写的一个计算函数和测试代码:

 

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.00.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.020.0, Image.Width, Image.Height);
    g.Clear(ARGBFromTColor(Color));
    g.DrawImage(Image, r);

    m.Rotate(
30);
    m.Scale(
1.80.8);
    m.Shear(
0.30.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;

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

        还有的时候,我们需要精确控制图形的变换,比如,以图形左上角为原点缩放、以图形中心点进行旋转等。由于GDI+的几何变换是以Graphics画布原点,而不是以图形左上角为原点进行的,所以必须使用平移进行坐标变换,然后采用相对坐标画图。比如,我们希望上面的例子以画布20,20为原点(即无论怎样变换,左上角坐标不变)进行线性变换:

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

    m.Translate(
2020);    // 坐标转换

    m.Rotate(
30);
    m.Scale(
1.80.8);
    m.Shear(
0.30.2);
    g.SetTransform(m);
    g.DrawImage(Image, 
00, Image.Width, Image.Height);  //用相对坐标画图

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

这个问题似乎很简单,但是我们忽略了一个变换顺序问题,前面所有的变换操作都是以默认顺序moPrepend进行的,如果改为moAppend是否符合我们的要求呢?答案是否!因为moPrepend是在上一个变换操作完成前进行的,也就是说无论是图形形状还是坐标位置,都是和上一变换操作同步进行的;而moAppend则是在上一变换操作完成后进行的,这时,图形形状和坐标点都已经变了,在已经改变了形状和坐标位置的基础上继续当前变换,肯定的不到我们预想的效果。下面的图是改变上面三个变换操作的顺序为moAppend后的运行结果:

为此,我们不能再用坐标变换后的相对坐标进行操作,而是需要在每个变换操作前预置坐标位置和变换操作后还原坐标位置,以便当前和后续变换都是正确的。Matrix已经提供了一个RotateAt,我们只要再写ScaleAt和ShearAt就行了,这样一来,无论操作顺序如何,变换操作位置都在我们的预期范围内。下面是这2个过程和测试代码:

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, 
2020, Image.Width, Image.Height);

    m.RotateAt(
30, GpPoint(20.020.0), moAppend);
    ScaleAt(m, 
1.80.82020, moAppend);
    ShearAt(m, 
0.30.22020, moAppend);

    g.SetTransform(m);
    g.DrawImage(Image, 
2020, 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;


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
WebBrowser 操作记要 (DELPHI)发布时间:2022-07-18
下一篇:
在Delphi2009中,forin循环都能用在什么地方?发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap