2

我想在画布上绘制文本。为了进行轮换,我使用了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;

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

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

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

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

4

3 回答 3

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;

更新:

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

作为对您的评论的回应,以下代码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;

可以简化为:

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;

这反过来又演变成这个一般例程:

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;
于 2013-10-06T15:10:09.787 回答
0
 procedure TmsLineWithArrow.DoDrawTo(const aCanvas: TCanvas;
  const aOrigin: TPointF);
var
 l_Proxy : TmsShape;
 l_OriginalMatrix: TMatrix;
 l_Matrix: TMatrix;
 l_Angle : Single;
 l_CenterPoint : TPointF;

 l_TextRect : TRectF;
begin
 inherited;

 aCanvas.BeginScene;  

 if (StartPoint <> FinishPoint) then
 begin
  l_OriginalMatrix := aCanvas.Matrix;
  try
   l_Proxy := TmsSmallTriangle.Create(FinishPoint);
   try
    // in Radian
    l_Angle := GetArrowAngleRotation;

    // create a point around which will rotate
    l_CenterPoint := TPointF.Create(FinishPoint.X, FinishPoint.Y);

    l_Matrix := l_OriginalMatrix;
    l_Matrix := l_Matrix * TMatrix.CreateTranslation(-l_CenterPoint.X,-l_CenterPoint.Y);
    l_Matrix := l_Matrix * TMatrix.CreateRotation(l_Angle);
    l_Matrix := l_Matrix * TMatrix.CreateTranslation(l_CenterPoint.X,l_CenterPoint.Y);

    aCanvas.SetMatrix(l_Matrix);

    l_Proxy.DrawTo(aCanvas, aOrigin);
   finally
    FreeAndNil(l_Proxy);
   end;//try..finally
  finally
    aCanvas.SetMatrix(l_OriginalMatrix);
    aCanvas.EndScene;
  end;
 end;//(StartPoint <> FinishPoint)
end;

此代码在 XE5 Firemonkey 应用程序中工作。所有来源在这里 https://bitbucket.org/ingword/mindstream

于 2014-07-30T19:36:08.647 回答
-1

对于感兴趣的人,在这里您可以找到我用于 FMX 的 C++Builder 版本(在 10.2 / Tokyo 上测试):

功能 :

// 在 pMainBitmap 上绘制旋转文本;Rot = 应该是 90° 的倍数!

void DrawRotatedText( TBitmap * pMainBitmap, TRectF TextDestRect, String StrTxt, int Rot )

(当然在以下代码之间添加“{”和“}”......编辑器发布此答案时总是出错......)

int SizeTextW = pMainBitmap->Canvas->TextWidth( StrTxt );
int SizeTextH = pMainBitmap->Canvas->TextHeight( StrTxt );
int SizeTextMax = (SizeTextW>SizeTextH)?SizeTextW:SizeTextH;
TRectF TheTextRect;
TheTextRect.init( 0, 0, SizeTextMax, SizeTextMax );

TBitmap * pBitmapText = new TBitmap( SizeTextMax, SizeTextMax );
if ( pBitmapText )
{
    /* background color used */
    pBitmapText->Clear( claBlack );

    pBitmapText->Canvas->BeginScene();
    // use same color than main bitmap for text
    pBitmapText->Canvas->Fill->Color = pMainBitmap->Canvas->Fill->Color;
    pBitmapText->Canvas->FillText(TheTextRect, StrTxt, false, 100,
        TFillTextFlags()/* << TFillTextFlag::RightToLeft*/, TTextAlign::Center,
        TTextAlign::Center);
    pBitmapText->Canvas->EndScene();
    // Canvas->EndScene must be done before doing bitmap rotate/flip...!
    if( Rot==180 )
        pBitmapText->FlipVertical( );
    else if ( Rot!=0 )
        pBitmapText->Rotate( Rot );

    int PosSrcX = 0;
    int PosSrcY = 0;
    if ( SizeTextW>SizeTextH )
        PosSrcX = (SizeTextMax-SizeTextH)/2;
    else
        PosSrcY = (SizeTextMax-SizeTextW)/2;
    TheTextRect.init( PosSrcX, PosSrcY, PosSrcX+SizeTextH, PosSrcY+SizeTextW );
    int iPosDestX = TextDestRect.left;
    int iPosDestY = TextDestRect.top;
    if ( (TextDestRect.right-TextDestRect.left)>SizeTextH )
        iPosDestX = iPosDestX+(TextDestRect.right-TextDestRect.left-SizeTextH)/2;
    if ( (TextDestRect.bottom-TextDestRect.top)>SizeTextW )
        iPosDestY = iPosDestY + (TextDestRect.bottom-TextDestRect.top-SizeTextW)/2;
    TextDestRect.left = iPosDestX;
    TextDestRect.top = iPosDestY;
    TextDestRect.right = TextDestRect.left+SizeTextH;
    TextDestRect.bottom = TextDestRect.top+SizeTextW;
    pMainBitmap->Canvas->DrawBitmap( pBitmapText, TheTextRect, TextDestRect, 100, true );

    delete( pBitmapText );
}
于 2018-03-07T16:05:57.590 回答