使用Canvas.TextOut有什么含义? [英] What are the implications of using Canvas.TextOut?

查看:80
本文介绍了使用Canvas.TextOut有什么含义?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简介



我的问题来自我过去几天一直在处理的一个相当有趣的问题。我最近问了一个有关



图.1 Canvas.TextOut





图2 DrawText



您可能需要o使用屏幕放大镜看得更近一些,但是在SomeText行中, Some Text 以及文本 T e 之间>稍有不同。






示例2



A略有不同更好的例子也许是在 Canvas.TextOut DrawText 与就地编辑器(TEdit)文本之间进行比较:





图3比较



您可以看到这里的区别更加明显。当使用 Canvas.TextOut 时,字符串 True 明显显示出文本字符之间更大的间距,其中 DrawText 就地编辑器完全相同地呈现文本。



使用 Canvas.TextOut ,我在调整检查器分隔符的大小以及显示和隐藏就地编辑器之间遇到各种可怕的文本不匹配的情况。如果我没有尝试过尝试其他的文本绘制方法,我想我永远也不会意识到这种差异并找到解决方案。重要的是要知道在将文本绘制到画布上时,我使用的字体设置与为原位编辑器定义的字体完全相同。



现在,我我使用的是 DrawText 而不是 Canvas.TextOut 一切都与就地编辑器以及我想要的方式一致。






问题



我的问题是 Canvas.TextOut 呈现文本与 DrawText 有何不同?从我的示例并处理我当前的问题,很明显, Canvas.TextOut 不会以与具有相同字体设置的TEdit相同的方式呈现文本,但是 DrawText 确实呈现了看似正确的文本。



这使我质疑的用法Canvas.TextOut ,如果它不能正确呈现文本,我是否应该总是使用 DrawText 代替?






测试演示



您可以使用以下代码对其进行测试:

  type 
TForm1 = class(TForm)
Edit1:TEdit;
过程FormCreate(Sender:TObject);
过程FormDestroy(Sender:TObject);
过程FormPaint(Sender:TObject);
私人
Font:TFont;
FRect:TRect;
public
{公开声明}
结尾;

var
Form1:TForm1;

实现

{$ R * .dfm}

过程TForm1.FormCreate(Sender:TObject);
开始
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);
结尾;

过程TForm1.FormDestroy(Sender:TObject);
开始
Font。免费;
结尾;

过程TForm1.FormPaint(Sender:TObject);
开始
Canvas.TextOut(10,10,‘Canvas.TextOut:[真]’);
DrawText(Canvas.Handle,PChar('DrawText:[True]'),Length('DrawText:[True]'),FRect,DT_LEFT);
结尾;

通过以上操作在全新的VCL项目上运行,我得到的结果如下:





图4测试演示



再次注意使用 Canvas.TextOut <时字符串 True 的间距/ code>,从我的角度来看,它明显不同于 DrawText TEdit 绘制方式



下图与图4相同,但放大了400%





图5测试演示放大了400%



Text <中,在 T e 之间看到了显着差异/ code>以及 True 中的 T r





图6文本使用指南放大了400%



您可以看到 T e 使用 DrawText 比使用 Canvas.TextOut (使用 ExtTextOut 。)





图7单词 True 根据准则放大了700%



您可以看到 T r 之间的字距更近了一个像素与 Canvas.T相比,使用 DrawText 和就地编辑器(TEdit) extOut (使用 ExtTextOut 。)






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



好:


Arial,Cambria,Candara,Comic Sans MS,Consolas,Courier,Courier New,
Fixedsys,乔治亚州,Lucida Console,Lucida Sans Unicode,Microsoft Sans
Serif,Tahoma,Terminal和Times New罗马。


坏:


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


好的字体是看起来可以使文本呈现相同字体的字体就像 DrawText 一样,Inpace编辑器(TEdit)控件也使用 Canvas.TextOut 进行操作。不好的结果表明 Canvas.TextOut 呈现的文本与其他方法略有不同。



虽然我不太确定,但这里还是有一些线索,不过我还是添加了它,以防万一。

解决方案

可观察到的差异是由于使用了不同的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
);




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


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



这些差异在某些字符组合中尤其明显,例如 T 和在 T AV ,其中一个 V 可以容纳 A 。不同的字体也具有不同的默认字距,这就是为什么某些字体使用这两种功能在外观上呈现相同效果的原因,而有些字体则没有。字距调整还取决于字体大小。例如,以 9 pt Arial 渲染的字符 AV 两个函数具有相同的输出,而 12 pt Arial 将导致不同的输出。



下图的第一行使用 ExtTextOut 进行无字距绘制,第二行使用 DrawText



< img src = https://i.stack.imgur.com/wF1oM.png alt =在此处输入图片描述>


Introduction

My question comes from a rather interesting problem I have been dealing with for the past few days. I recently asked a question regarding Writing a custom property inspector - How to handle inplace editor focus when validating values?

I have since made some nice progress with my control such as adding a divider in the middle to separate between Name and Value rows, and importantly the divider can be used to resize the two columns.

Here is where my problems started, having the inplace editor visible whilst resizing the divider caused a slight slow down on my control. So I further changed the code to only show the inplace editor if the divider is not been resized. So essentially, I used Canvas.TextOut to draw my values as strings, if a row is selected then the Inplace editor is shown above. The inplace editor becomes hidden if the divider is been resized, once the resize operation has complete the inplace editor becomes visible again.

Whilst this solved the slight slowdown issue I mentioned, I was faced with a new problem in that the text from the inplace editor (which is basically a TEdit) differed slightly to the text that I was drawing using Canvas.TextOut


Example 1

The difference is quite subtle but if you look close enough you can just see it:

fig.1 Canvas.TextOut

fig.2 DrawText

You may need to use a screen magnifier to look more closer, but with the SomeText row it is more noticeable in that the spacing between Some and Text and also between the T and e in Text is slightly different.


Example 2

A slightly better example is perhaps comparing between Canvas.TextOut and DrawText to the inplace editor (TEdit) text:

fig.3 Comparison

As you can see the difference here is much more prominent. The string True clearly shows much larger spacing between the text characters when using Canvas.TextOut, where as the DrawText and inplace editor render text exactly alike.

When I was using Canvas.TextOut I was getting all kinds of horrible text mismatches between resizing my inspector divider and showing and hiding the inplace editor. Had I not experimented and tried alternative text drawing methods I don't think I would have ever realised the difference and found a solution. It is important to know that I was using the exact same Font settings when drawing my text to the canvas as the Font I had defined for the inplace editor.

Now that I am using DrawText instead of Canvas.TextOut everything is working in unison with the inplace editor and exactly how I want it to.


Question

My question is what makes Canvas.TextOut render text so differently to DrawText? From my example and dealing with my current problem, it is clear that Canvas.TextOut does not render the text in the same way that a TEdit with the same Font settings does, but DrawText does render text seemingly the correct way.

This makes me question the use of Canvas.TextOut, if it does not render text correctly should I always look to use DrawText instead?


Test Demo

You can test this for yourself with the following code:

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;

With the above running on a completely new VCL Project, the result I get is as follows:

fig.4 Test Demo

Again notice the spacing in the string True when using Canvas.TextOut, from my end it is clearly different to DrawText and the way that the TEdit draws its text.

The below is the same image as fig.4 but zoomed in at 400%

fig.5 Test Demo zoomed at 400%

Noticeable differences are seen between the T and e in Text and also T and r in True.

fig.6 The word 'Text' zoomed in at 400% with guidelines

You can see the kerning between the T and e is one pixel closer with DrawText than with Canvas.TextOut (which uses ExtTextOut.)

fig.7 The word True zoomed in at 700% with guidelines

You can see the kerning between the T and r is one pixel closer with DrawText and the Inplace Editor (TEdit) than with Canvas.TextOut (which uses ExtTextOut.)


I have tested several different fonts and here are my findings:

Good:

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

Bad:

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

The good fonts are the ones that appear to render text the same way as DrawText and the Inpace Editor (TEdit) controls do using Canvas.TextOut. The bad ones show that Canvas.TextOut renders text slightly different to the other methods.

There may some clue here although I am not too sure, but I am adding it anyway just in case.

解决方案

Observed difference is due to using different WinAPI text rendering functions and their behavior. Specifically character kerning

In typography, kerning (less commonly mortising) is the process of adjusting the spacing between characters in a proportional font, usually to achieve a visually pleasing result. Kerning adjusts the space between individual letter forms, while tracking (letter-spacing) adjusts spacing uniformly over a range of characters.

  1. DrawText

The DrawText function draws formatted text in the specified rectangle. It formats the text according to the specified method (expanding tabs, justifying characters, breaking lines, and so forth).

  1. ExtTextOut (used by Canvas.TextOut)

ExtTextOut declaration:

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
);

If the lpDx parameter is NULL, the ExtTextOut function uses the default spacing between characters. The character-cell origins and the contents of the array pointed to by the lpDx parameter are specified in logical units. A character-cell origin is defined as the upper-left corner of the character cell.

Basically DrawText will automatically draw formatted text and that includes adjusting spacing between characters (kerning), while ExtTextOut will by default use default spacing between characters (no-kerning). If you want to adjust spacing between characters you will have to calculate and provide kerning array (lpDx) parameter.

Those differences are especially visible with some character combinations like T and small letters that visually fit under T, or AV where one V fits over A. Different fonts also have different default kernings and that is reason why some fonts have visually same rendering using both functions and some not. Kerning also depends on font size. For instance characters AV rendered with Arial at 9 pt will have same output with both functions, while Arial at 12 pt will result in different outputs.

First line in following image is drawn with no-kerning using ExtTextOut and second line with automatic kerning using DrawText.

这篇关于使用Canvas.TextOut有什么含义?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆