На этом шаге мы рассмотрим задание характеристик шрифта.
Рассмотрим теперь вопрос о том, как выводить текстовую информацию с различными типами шрифтов. Удобнее всего задать параметры шрифта при помощи функции CreateFontIndirect, параметром которой является указатель на структуру LOGFONT. Хотя название функции и начинается со слова Create, речь идет не о создании, а скорее об изменении существующего шрифта согласно заданным параметрам. Существует и другая функция CreateFont, которая, на наш взгляд, менее удобна при использовании на ассемблере. Выбор нужного шрифта осуществляется функцией SelectObject.
Начнем с того, что разберем поля этой структуры.
LOGFONT STRUC LfHeight DWORD ? LfWidth DWORD ? LfEscapement DWORD ? LfOrientation DWORD ? LfWeight DWORD ? LfItalic DB ? LfUnderline DB ? LfStrikeOut DB ? LfCharSet DB ? LfOutPrecision DB ? LfClipPrecision DB ? LfQuality DB ? LfPitchAndFamily DB ? LfFaceName DB 32 DUP(0) LOGFONT ENDS
Здесь:
Некоторые из перечисленных значений перечислены в разделе Delphi на 42 шаге "Графические инструменты в Delphi. Дополнительные возможности API-функций".
Обратимся к примеру задания своего шрифта (результат работы программы - на рисунке 1). Однако поскольку большая часть программы будет совпадать с аналогичной частью предыдущих программ, сы приведем здесь только необходимые фрагменты. Рассмотрим сначала фрагмент, выполняющийся при получении сообщения WM_PAINT.
WMPAINT: ;-------------------------- PUSH OFFSET PNT PUSH DWORD PTR [EBP+08H] CALL BeginPaint@8 MOV CONT,EAX ;Сохранить контекст (дескриптор) ;------------------------ цвет фона = цвет окна PUSH RGBW PUSH EAX CALL SetBkColor@8 ;------------------------ цвет текста (красный) PUSH RGBT PUSH CONT CALL SetTextColor@8 ;Здесь определение координат. MOV XT,120 MOV YT,140 ;Задать (создать) шрифт. MOV lg.LfHeight,12 ;Высота шрифта. MOV lg.LfWidth,9 ;Ширина шрифта. MOV lg.LfEscapement,900 ;Ориентация - MOV lg.LfOrientation,0 ;вертикальная. MOV lg.LfWeight,400 ;Толщина линий шрифта. MOV lg.LfItalic,0 ;курсив MOV lg.LfUnderline,0 ;Подчеркивание. MOV lg.LfStrikeOut,0 ;Перечеркивание. MOV lg.LfCharSet,0 ;Набор шрифтов. MOV lg.LfOutPrecision,0 MOV lg.LfClipPrecision,0 MOV lg.LfQuality,2 MOV lg.LfPitchAndFamily,0 PUSH OFFSET lg ;Задать название шрифта. PUSH OFFSET NFONT PUSH OFFSET lg.LfFaceName CALL COPYSTR CALL CreateFontIndirectA@4 ;-------------------- выбрать созданный объект PUSH EAX PUSH CONT CALL SelectObject@8 PUSH EAX ;-------------------- вычислить длину текста в пикселах текста PUSH OFFSET TEXT CALL LENSTR ;-------------------- вывести текст PUSH EBX PUSH OFFSET TEXT PUSH YT PUSH XT PUSH CONT CALL TextOutA@20 ;Удалить объект "FONT". ;Идентификатор уже в стеке. CALL DeleteObject@4 ;-------------------- закрыть контекст PUSH OFFSET PNT PUSH DWORD PTR [EBP+08H] CALL EndPaint@8 MOV EAX, 0 JNP FINISH
Рис.1. Результат выполнения приложения
Как видно из фрагмента, создание шрифта производится по следующей схеме:
Поле LfFaceName структуры LOGFONT должно содержать название шрифта. Если такого шрифта нет, выводится шрифт по умолчанию. Название шрифта у нас задано в строке NFONT, и мы копируем его в поле LfFaceName при помощи функции COPYSTR, текст которой приводится ниже:
;Процедура копирования одной строки в другую. ;Строка, куда копировать [ЕВР+08Н]. ;Строка, что копировать [ЕВР+0СН]. COPYSTR PROC PUSH EBP MOV EBP,ESP MOV ESI,DWORD PTR [EBP+0CH] MOV EDI,DWORD PTR [EBP+08H] L1: MOV AL, BYTE PTR [ESI] MOV BYTE PTR [EDI], AL CMP AL,0 JE L2 INC ESI INC EDI JMP L1 L2: POP EBP RET 8 COPYSTR ENDP
Данная процедура не учитывает длину строки, в которую производится копирование. Лучше всего это сделать, включив еще один параметр - максимальное количество символов, которое может быть скопировано. Попробуйте это сделать.
В заключение раздела мы рассмотрим один очень важный вопрос. При разборе предыдущих примеров этот вопрос, скорее всего, у вас не возникал, и вот почему. Весь вывод информации происходил в программе по получению сообщения WM_PAINT. В реальных программах вывод информации в окно может происходить по различным событиям и из различных процедур. Кроме того, если информации в окне много, то непосредственный вывод при помощи функции TextOut довольно медленный. Чтобы воспроизводить содержимое окна, необходимо где-то запомнить это содержимое. Возникает проблема сохранения информации (и не только текстовой), находящейся в окне.
Если кто-то программировал для операционной системы MS-DOS, то там подобная проблема также возникает. Решается она следующим образом: используется фоновая видеостраница, на которую выводится вся информация. Затем фоновая страница копируется на видимую страницу. При этом создается впечатление, что информация появляется на экране мгновенно. В качестве фоновой страницы может быть использована как область ОЗУ, так и область видеопамяти.
Аналогично в операционной системе Windows образуется виртуальное окно, и весь вывод информации производится туда. Затем по приходе сообщения WM_PAINT содержимое виртуального окна копируется на реальное окно. В целом общая схема такова:
Изложенная теория будет применена на практике в следующих шагах.
На следующем шаге мы рассмотрим графические образы.