使用Canvas.TextOut有什么含义?

Cra*_*aig 19 delphi delphi-xe7

介绍

我的问题来自于过去几天我一直在处理的一个相当有趣的问题.我最近问了一个关于编写自定义属性检查器的问题 - 如何在验证值时处理内部编辑器焦点?

我已经在我的控件上取得了一些不错的进步,比如在中间添加一个分隔符来分隔Name和Value行,重要的是分隔符可以用来调整两列的大小.

这是我的问题开始的地方,让inplace编辑器可见,同时调整分隔符的大小导致我的控制稍微减慢.所以我进一步改变了代码,只显示了inplace编辑器,如果没有调整分隔符的大小.基本上,我习惯Canvas.TextOut将我的值绘制为字符串,如果选择了一行,则上面显示了Inplace编辑器.如果已调整分隔符大小,则inplace编辑器将隐藏,一旦调整大小操作完成,就地编辑器将再次可见.

虽然这解决了我提到的轻微减速问题,但我遇到了一个新问题,因为来自内部编辑器(基本上是TEdit)的文本与我正在使用的文本略有不同 Canvas.TextOut


例1

差异非常微妙,但如果你看得足够近,你可以看到它:

图1 Canvas.TextOut

在此输入图像描述

图2 DrawText

您可能需要使用屏幕放大镜看起来更近,但与SomeText排它是更明显的间距之间SomeText也的TeText略有不同.


例2

稍微好一点的例子或许之间的比较Canvas.TextOut,并DrawText在内置的编辑器(TEDIT)文本:

在此输入图像描述

图3比较

正如你所看到的,这里的区别更为突出.字符串True清晰地显示了文本字符在使用时间距更大Canvas.TextOut,其中DrawTextinplace editor文本字符完全相同.

当我使用时,Canvas.TextOut我在调整检查器分隔符的大小以及显示和隐藏内部编辑器之间遇到了各种可怕的文本不匹配.如果我没有尝试并尝试替代文本绘制方法,我认为我不会意识到差异并找到了解决方案.重要的是要知道我在将文本绘制到画布时使用完全相同的字体设置作为我为内置编辑器定义的字体.

现在我正在使用DrawText而不是Canvas.TextOut一切正在与inplace编辑器一致工作,以及我想要它的确切方式.


我的问题是什么使Canvas.TextOut渲染文本变得如此不同DrawText?从我的例子和处理我当前的问题,很明显,Canvas.TextOut不会以与具有相同Font设置的TEdit相同的方式DrawText呈现文本,但是渲染文本看起来似乎是正确的方式.

这让我质疑使用Canvas.TextOut,如果它没有正确呈现文本,我应该总是使用DrawText吗?


测试演示

您可以使用以下代码自行测试:

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormPaint(Sender: TObject);
  private
    FFont: TFont;
    FRect: TRect;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FFont       := TFont.Create;
  FFont.Color := clNavy;
  FFont.Name  := 'Segoe UI';
  FFont.Size  := 9;
  FFont.Style := [];
  FRect       := Rect(10, 30, 100, 100);

  Canvas.Font.Assign(FFont);
  Edit1.Font.Assign(FFont);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FFont.Free;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin 
  Canvas.TextOut(10, 10, 'Canvas.TextOut: [True]');
  DrawText(Canvas.Handle, PChar('DrawText: [True]'), Length('DrawText: [True]'), FRect, DT_LEFT);
end;
Run Code Online (Sandbox Code Playgroud)

以上运行在一个全新的VCL项目上,我得到的结果如下:

在此输入图像描述

图4测试演示

再次注意True使用时字符串中的间距Canvas.TextOut,从我的结尾来看,它DrawTextTEdit绘制文本的方式明显不同.

以下是与图4相同的图像,但放大了400%

在此输入图像描述

图5测试演示放大400%

明显的差异被视为之间TeText,也TrTrue.

在此输入图像描述

图6"文字"这个词用指南放大了400%

你可以看到字距之间的Te与一个像素更接近DrawTextCanvas.TextOut(使用ExtTextOut).

在此输入图像描述

图7这个词True用指南放大了700%

你可以看到字距之间Tr是一个像素更接近与DrawText和内置的编辑器(TEDIT)比Canvas.TextOut(使用ExtTextOut).


我测试了几种不同的字体,这是我的发现:

好:

Arial,Cambria,Candara,Comic Sans MS,Consolas,Courier,Courier New,Fixedsys,Georgia,Lucida Console,Lucida Sans Unicode,Microsoft Sans Serif,Tahoma,Terminal和Times New Roman.

坏:

Calibri,Corbel,Myriad Pro,Segoe UI,Trebuchet MS和Verdana.

好的字体似乎是以DrawText与Inpace Editor(TEdit)控件使用相同的方式呈现文本的字体Canvas.TextOut.坏的表明Canvas.TextOut渲染文本与其他方法略有不同.

虽然我不太确定,但这里可能有一些线索,但无论如何我都会添加它以防万一.

Dal*_*kar 7

观察到的差异是由于使用不同的WinAPI文本呈现函数及其行为.特别是字符字距调整

在排版中,字距调整(不太常见的榫眼)是以比例字体调整字符之间的间距的过程,通常是为了获得视觉上令人愉悦的结果.字距调整单个字母表单之间的间距,而跟踪(字母间距)在一系列字符上均匀调整间距.

  1. DrawText的

DrawText函数在指定的矩形中绘制格式化文本.它根据指定的方法格式化文本(扩展选项卡,对齐字符,断行等).

  1. ExtTextOut(用于Canvas.TextOut)

ExtTextOut 宣言:

BOOL ExtTextOut(
  _In_       HDC     hdc,
  _In_       int     X,
  _In_       int     Y,
  _In_       UINT    fuOptions,
  _In_ const RECT    *lprc,
  _In_       LPCTSTR lpString,
  _In_       UINT    cbCount,
  _In_ const INT     *lpDx
);
Run Code Online (Sandbox Code Playgroud)

如果lpDx参数为NULL,则ExtTextOut函数使用字符之间的默认间距.字符单元起源和lpDx参数指向的数组内容以逻辑单位指定.字符单元格原点定义为字符单元格的左上角.

基本上DrawText会自动绘制格式化文本,包括调整字符间距(字距调整),而ExtTextOut默认情况下会使用字符间的默认间距(no-kerning).如果要调整字符之间的间距,则必须计算并提供字距调整array(lpDx)参数.

这些差异特别明显,有些字符组合T和视觉上适合的小字母T,或者适合的AV地方.不同的字体也有不同的默认字距,这就是为什么有些字体使用两种功能在视觉上相同的渲染而有些不是.字距也取决于字体大小.例如 ,使用at 渲染的字符将具有与两个函数相同的输出,而at 将导致不同的输出.VAAVArial9 ptArial12 pt

下图中的第一行使用no-kerning绘制,ExtTextOut第二行使用自动字距调整DrawText.

在此输入图像描述