Firemonkey 旋转文本

Chr*_*sto 2 delphi firemonkey delphi-xe4 firemonkey-fm3

我想在画布上绘制文本。为了进行轮换,我使用了https://forums.embarcadero.com/thread.jspa?messageID=440010 中的以下代码

//bm is a TImage    
a := 45;
c:= bm.Canvas;
CurrentM := c.Matrix;
a:=-radian(a);
m.m11:= cos(a); m.m12:=sin(a); m.m13:=0;
m.m21:=-sin(a); m.m22:=cos(a); m.m23:=0;
m.m31:=0;       m.m32:=0;      m.m33:=1;
c.setmatrix(M);

c.BeginScene;
    c.filltext(rectf(100,100,5000,5000), 'test rotated string', false,1,[],ttextalign.taLeading,ttextalign.taLeading);
c.EndScene;
Run Code Online (Sandbox Code Playgroud)

这工作正常。我已将矩形的右侧和底部设置为 5000,这样我就不必担心矩形太小了。

问题是我现在想更改我的 TextAlignment 属性。所以要从右到左绘制文本,我必须调整我的矩形,然后按以下方式绘制它:

c.BeginScene;
    c.filltext(rectf((100 - 5000),100,100,5000), 'test rotated string', false,1,[],ttextalign.taTrailing,ttextalign.taLeading);
c.EndScene;
Run Code Online (Sandbox Code Playgroud)

所以基本上我移动了矩形 TopLeft 的 x 值并将其移回 5000(我再次使用 5000 来确保我的文本适合)。然后,我将矩形右下角的 x 值设置为 x 值在上一个示例矩形的 TopLeft 中的位置。这适用于 0 度旋转,但是一旦我更改了度数,我就不会在正确的位置绘制文本。我认为这是因为文本将围绕矩形的 TopLeft 位置旋转(更改为使文本从右向左书写)。

NGL*_*GLN 5

我认为这是因为文本将围绕矩形的 TopLeft 位置旋转

不,旋转以画布的当前原点为中心。默认情况下,即坐标0, 0,但可以通过当前设置的变形矩阵进行更改。典型的方法是:选择一个旋转中心,将原点移到该中心,旋转,移回改变的原点,然后绘制。请参阅TControl.MatrixChanged以供参考。但是还有很多其他的方法。

以下是如何在表单中从左下角到右上角绘制文本的示例:

在此处输入图片说明

procedure TForm1.FormPaint(Sender: TObject; Canvas: TCanvas;
  const ARect: TRectF);
var
  Angle: Single;
  R: TRectF;
  S: String;
  H: Single;
  Matrix: TMatrix;
begin
  Canvas.Fill.Color := TAlphaColors.Black;
  Angle := -ArcTan2(ClientHeight, ClientWidth);
  R := ClientRect;
  S := 'Text from bottom-left...';
  H := Canvas.TextHeight(S);
  Matrix := CreateRotationMatrix(Angle);
  Matrix.m31 := Sin(Angle) * (ClientHeight - H);
  Matrix.m32 := ClientHeight  * (1 - Cos(Angle));
  Canvas.SetMatrix(Matrix);
  Canvas.FillText(R, S, False, 1, [], TTextAlign.taLeading,
    TTextAlign.taTrailing);
  S := '...to top-right';
  Matrix.m31 := ClientWidth * (1 - Cos(Angle)) + Sin(Angle) * H;
  Matrix.m32 := -Sin(Angle) * ClientWidth;
  Canvas.SetMatrix(Matrix);
  Canvas.FillText(R, S, False, 1, [], TTextAlign.taTrailing,
    TTextAlign.taLeading);
end;
Run Code Online (Sandbox Code Playgroud)

更新:

此代码尚未考虑已经移动的原点。

为了响应您的评论,以下代码50, 100使用上面解释的方法,在窗体上任意定位的 PaintBox 上从坐标向下绘制文本,围绕该点旋转 90°。

procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
const
  S = 'Hello World';
var
  R: TRectF;
  OriginalMatrix: TMatrix;
  ShiftMatrix: TMatrix;
  RotationMatrix: TMatrix;
  ShiftBackMatrix: TMatrix;
  Matrix: TMatrix;
begin
  PaintBox1.Canvas.Fill.Color := TAlphaColors.Black;
  R.Right := 50;
  R.Bottom := 100;
  R.Left := R.Right - 5000;
  R.Top := R.Bottom - 5000;
  OriginalMatrix := PaintBox1.Canvas.Matrix;
  ShiftMatrix := IdentityMatrix;
  ShiftMatrix.m31 := -R.Right;
  ShiftMatrix.m32 := -R.Bottom;
  RotationMatrix := CreateRotationMatrix(DegToRad(-90));
  ShiftBackMatrix := IdentityMatrix;
  ShiftBackMatrix.m31 := R.Right;
  ShiftBackMatrix.m32 := R.Bottom;
  Matrix := MatrixMultiply(RotationMatrix, ShiftBackMatrix);
  Matrix := MatrixMultiply(ShiftMatrix, Matrix);
  Matrix := MatrixMultiply(Matrix, OriginalMatrix);
  PaintBox1.Canvas.SetMatrix(Matrix);
  PaintBox1.Canvas.FillText(R, S, False, 1, [], TTextAlign.taTrailing,
    TTextAlign.taTrailing);
  PaintBox1.Canvas.SetMatrix(OriginalMatrix);
end;
Run Code Online (Sandbox Code Playgroud)

可以简化为:

procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
const
  S = 'Hello World';
var
  R: TRectF;
  SaveMatrix: TMatrix;
  Matrix: TMatrix;
begin
  PaintBox1.Canvas.Fill.Color := TAlphaColors.Black;
  R := RectF(-Canvas.TextWidth(S), -Canvas.TextHeight(S), 0, 0);
  SaveMatrix := PaintBox1.Canvas.Matrix;
  Matrix := CreateRotationMatrix(DegToRad(-90));
  Matrix.m31 := 50;
  Matrix.m32 := 100;
  PaintBox1.Canvas.MultyMatrix(Matrix);
  PaintBox1.Canvas.FillText(R, S, False, 1, [], TTextAlign.taTrailing,
    TTextAlign.taTrailing);
  PaintBox1.Canvas.SetMatrix(SaveMatrix);
end;
Run Code Online (Sandbox Code Playgroud)

这反过来演变成这个通用例程:

procedure DrawRotatedText(Canvas: TCanvas; const P: TPointF; RadAngle: Single;
  const S: String; HTextAlign, VTextAlign: TTextAlign);
var
  W: Single;
  H: Single;
  R: TRectF;
  SaveMatrix: TMatrix;
  Matrix: TMatrix;
begin
  W := Canvas.TextWidth(S);
  H := Canvas.TextHeight(S);
  case HTextAlign of
    TTextAlign.taCenter:   R.Left := -W / 2;
    TTextAlign.taLeading:  R.Left := 0;
    TTextAlign.taTrailing: R.Left := -W;
  end;
  R.Width := W;
  case VTextAlign of
    TTextAlign.taCenter:   R.Top := -H / 2;
    TTextAlign.taLeading:  R.Top := 0;
    TTextAlign.taTrailing: R.Top := -H;
  end;
  R.Height := H;
  SaveMatrix := Canvas.Matrix;
  Matrix := CreateRotationMatrix(RadAngle);
  Matrix.m31 := P.X;
  Matrix.m32 := P.Y;
  Canvas.MultyMatrix(Matrix);
  Canvas.FillText(R, S, False, 1, [], HTextAlign, VTextAlign);
  Canvas.SetMatrix(SaveMatrix);
end;

procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
begin
  PaintBox1.Canvas.Fill.Color := TAlphaColors.Black;
  DrawRotatedText(PaintBox1.Canvas, PointF(50, 100), DegToRad(-90),
    'Hello world', TTextAlign.taTrailing, TTextAlign.taTrailing);
end;
Run Code Online (Sandbox Code Playgroud)