Шаг 21.
Выбор шрифта

    На этом шаге мы рассмотрим задание характеристик шрифта.

    Рассмотрим теперь вопрос о том, как выводить текстовую информацию с различными типами шрифтов. Удобнее всего задать параметры шрифта при помощи функции 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 содержимое виртуального окна копируется на реальное окно. В целом общая схема такова:

  1. При создании окна:
    • создается совместимый контекст устройства. Для этого используется функция CreateCompatibieDC. Полученный контекст следует запомнить;
    • создается карта бит, совместимая с данным контекстом. Для этой цели применяется функция CreateCompatibleBitmap;
    • выбирается кисть цветом, совпадающим с цветом основного окна;
    • создается битовый шаблон путем выполнения растровой операции с использованием выбранной кисти. Выполняется функцией PatBlt.
  2. Вся информация выводится в виртуальное окно и дается команда перерисовки окна. Эти действия выполняются функцией InvalidateRect.
  3. При получении сообщения WM_PAINT содержимое виртуального окна копируется на реальное окно, посредством функции BitBlt.

    Изложенная теория будет применена на практике в следующих шагах.

    На следующем шаге мы рассмотрим графические образы.




Предыдущий шаг Содержание Следующий шаг