Каковы последствия использования Canvas.TextOut?
Введение
Мой вопрос исходит из довольно интересной проблемы, с которой я имел дело в течение последних нескольких дней. Я недавно задал вопрос о Написание персонализированного инспектора свойств - Как обрабатывать фокус редактора inplace при проверке значений?
С тех пор я немного улучшил свой контроль, например, добавив разделитель посередине для разделения между строками Name и Value, и, что важно, разделитель может использоваться для изменения размера двух столбцов.
Вот где начались мои проблемы, поскольку видимый редактор inplace пока изменял размер разделителя, что немного снизило мой контроль. Поэтому я еще больше изменил код, чтобы отображать только редактор inplace, если разделитель не был изменен. Поэтому, по существу, я использовал Canvas.TextOut
для рисования моих значений в виде строк, если выбрана строка, тогда редактор Inplace показан выше. Редактор inplace становится скрытым, если размер разделителя был изменен, после завершения операции изменения размера редактор inplace снова становится видимым.
В то время как это решило проблему небольшого замедления, о которой я упоминал, мне пришлось столкнуться с новой проблемой в том, что текст из редактора inplace (который в основном представляет собой TEdit) немного отличался от текста, который я рисовал, используя Canvas.TextOut
Пример 1
Разница довольно тонкая, но если вы посмотрите достаточно близко, вы можете просто увидеть ее:
![]()
fig.1 Canvas.TextOut
![введите описание изображения здесь]()
fig.2 DrawText
Вам может потребоваться использовать экранную лупу, чтобы выглядеть ближе, но с рядом SomeText более заметно, что расстояние между Some
и Text
, а также между T
и e
в Text
немного отличается.
Пример 2
Немного лучший пример - это сравнение между Canvas.TextOut
и DrawText
с текстом редактора inplace (TEdit):
![введите описание изображения здесь]()
fig.3 Сравнение
Как вы можете видеть, разница здесь гораздо более заметна. Строка True
наглядно показывает гораздо большее расстояние между текстовыми символами при использовании Canvas.TextOut
, где в качестве текста текста DrawText
и inplace editor
точно совпадают.
Когда я использовал Canvas.TextOut
, я получал всевозможные ужасные текстовые несоответствия между изменением размера моего делителя-инспектора и показом и скрытием редактора inplace. Если бы я не экспериментировал и не пытался использовать альтернативные методы рисования текста, я не думаю, что когда-либо бы понял разницу и нашел решение. Важно знать, что я использовал точные настройки шрифта при рисовании моего текста на холсте в качестве шрифта, который я определил для редактора inplace.
Теперь, когда я использую DrawText
вместо Canvas.TextOut
, все работает в унисон с редактором inplace и точно, как я хочу.
Вопрос
Мой вопрос заключается в том, что делает Canvas.TextOut
визуализировать текст так иначе, как DrawText
? Из моего примера и решения моей текущей проблемы ясно, что Canvas.TextOut
не отображает текст так же, как 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;
С приведенным выше запуском в совершенно новом проекте VCL результат, который я получаю, выглядит следующим образом:
![введите описание изображения здесь]()
fig.4 Test Demo
Снова обратите внимание на интервал в строке True
при использовании Canvas.TextOut
, с моего конца он явно отличается от DrawText
и способ, которым TEdit
рисует свой текст.
Ниже показано то же изображение, что и на рисунке 4, но увеличено на 400%
![введите описание изображения здесь]()
fig.5 Test Demo увеличено на 400%
Заметные различия наблюдаются между T
и e
в Text
, а также T
и r
в True
.
![введите описание изображения здесь]()
fig.6 Слово "Текст" увеличено на 400% с рекомендациями
Вы можете видеть, что кернинг между T
и e
ближе к пикселю с DrawText
, чем к Canvas.TextOut
(который использует ExtTextOut
.)
![введите описание изображения здесь]()
fig.7 Слово True
увеличилось на 700% с рекомендациями
Вы можете видеть, что кернинг между T
и r
находится на один пиксель ближе к DrawText
и редактору Inplace (TEdit), чем к Canvas.TextOut
(который использует ExtTextOut
.)
Я тестировал несколько разных шрифтов, и вот мои выводы:
Хорошо:
Arial, Cambria, Candara, Comic Sans MS, Consolas, Courier, Courier New, Fixedsys, Грузия, Lucida Console, Lucida Sans Unicode, Microsoft Sans Сериф, Тахома, Терминал и Times New Roman.
Плохо:
Calibri, Corbel, Myriad Pro, Segoe UI, Trebuchet MS и Verdana.
Хорошие шрифты - это те, которые, как представляется, отображают текст так же, как DrawText
, а элементы управления пространственным редактором (TEdit) используют Canvas.TextOut
. Плохие показывают, что Canvas.TextOut
делает текст немного отличающимся от других методов.
Здесь может быть какая-то подсказка, хотя я не слишком уверен, но я все равно добавляю ее на всякий случай.
Ответы
Ответ 1
Наблюдаемая разница связана с использованием различных функций визуализации текста WinAPI и их поведения. В частности, символ kerning
В типографии кернинг (реже врезной) - это процесс регулировка расстояния между символами пропорционального шрифта, обычно для достижения визуально приятного результата. Кернинг регулирует пространство между отдельными формами букв, а отслеживание (расстояние между буквами) равномерно распределяет интервал по диапазону символов.
Функция DrawText рисует отформатированный текст в указанном прямоугольнике. Он форматирует текст в соответствии с указанным методом (расширение вкладок, оправдание символов, нарушение линий и т.д.).
- 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
. Различные шрифты также имеют разные ядра по умолчанию, и это причина, по которой некоторые шрифты имеют визуально одинаковый рендеринг с использованием обеих функций, а некоторые нет. Кернинг также зависит от размера шрифта. Например, символы AV
, отображаемые с помощью Arial
at 9 pt
, будут иметь одинаковый вывод с обеими функциями, а Arial
at 12 pt
приведет к разным выходам.
Первая строка следующего изображения рисуется без кернинга с использованием ExtTextOut
и второй строки с автоматическим кернированием с использованием DrawText
.
![введите описание изображения здесь]()