Вывод текста и шрифты mfc


Вывод текста на дисплей Nokia 1616

Завершающая Статья по работе с дисплеем от Nokia 1616

Образы символов шрифта

Для того что бы выводить текст нам понадобятся изображения символов шрифта (по сути те же самые иконки из предидущего поста, разве что размер не так жёстко фиксирован). Для их получения отправляемся за проектом LPC1343 CodeBase и используем утилиту TheDotFactory. С её помощью вы можете сформировать «образы» любых, установленных в системе шрифтов. Сама утилита «качественно» и просто поддерживает только латинницу. Русские символы ей тоже можно сгенерировать, но иногда прийдется допиливать напильником таблицу символов.

Упаковываем образы символов шрифта

467 байт) что составляет 43.4% от общего размера данных (1078 байт).
Да, мне было нечего делать и в итоге с помощью компилятора и небольшого кода:

Переводим выравнивание на границу байта со строк на символ целиком и шрифт упаковывается до 650 байт, а избыточной информации получатся 316 бит (

39.4 байт) что составляет 6.1%. Это уже гораздо лучше. Ну, или по крайне мере терпимо. а то что внешний вид исходного кода потерялся — думаю переживём. Главное здесь что код вывода символа притерпит минимум изменений. Но а поскольку постоянно хочется иметь что-то универсальное, то код написан с возможности работы как с упакованными, так и не упакованными шрифтами. Нуда, ещё и моноширинные поддерживать умеем, но не будем вдаваться в подробности. Всю эту информацию о шрифте надо сохранять, для чего дорабатываем структуру описания шрифта.
В архиве прилагается парочка шрифтов для ознакомления с соответствующими им описателями.

Вывод символов

И так, сердцем по выводу текста, является функция вывода одного символа. Она похожа на вывод иконки, разве что узнает размеры самостоятельно и учитывает разные способы выравнивания.

Была идея вообще из иконок сделать шрифт. Идея вполне разумная и реализация имеет право на существование. Но не сделал, что бы не заминять установленный шрифт, ведь вывод текста и иконок может идти поочерёдно. Это конешно решается запоминанием раннее установленного шрифта. Вообщем не помню я почему не сделал так :)

Вывод строки

Ясное дело что посимвольно выводить не удобно, надо чего-то большего. Например вывода строки:

Расстояние между символами по хорошему тоже в харрактеристику шрифта надо поместить, но пока так.
Посколько шрифты имеют разную длину символов, то при выводе разных строк без предварительного стирания, у нас останется мусор от прошлого вывода. Если предварительно стереть всё пространство, то возникнет неприятное мерцание даже при выводе одной и той же строки. По этому полезной будет функция вывода строки в области:
Тут нам пригадилалась установка области отсечения. Видь если область недостаточного размера для вмещения строки и не прилегает к границе дисплея, то при выводе мы вылезем куда не положено. Можно было бы проверяьт при выводе каждого символа, но зачем, если уже есть более подходящее средство.
Ну и выводим:

Вот собственно и всё. Видео было в посте О том как делать не стоит. Фотку обновил, теперь кликабельно на 2304х3072 пикселей 2.62МБ.

  • Nokia 1616,
  • LCD
  • +5
  • 02 августа 2011, 21:38
  • angel5a
  • 2

Комментарии ( 21 )

Мощно задвинул. Внушает.

Раз такая обширная тема, то я завел специализированный бложек, где будут собираться материалы по разным дисплеям, а также методикам работы с графикой. И перетащил весь цикл постов туда.

Если будешь их править, то предварительно вступи в этот блог:
we.easyelectronics.ru/blog/lcd_gfx/

Т.к. если это не сделать, то после редактирования поста (вдруг чего забыл) он обратно улетит в твой персональный блог. А это плохо, т.к. ссылка на него изменится и если на статью кто ссылался, то при переходе получит 404.

Урок 4. Вывод текста в SDL

В этой теме 0 ответов, 1 участник, последнее обновление Васильев Владимир Сергеевич 3 года/лет, 1 месяц назад.

Рано или поздно вам понадобиться выводить текстовую информацию в вашей программе. Например, это может быть количество очков в игре, служебные сообщения и т.п. В библиотеке SDL к сожалению вы не найдете практически никаких средств для вывода текста. Вообще по сути, SDL предоставляет очень мало средств для рисования: вывод точки, вывод прямоугольного изображения и все… Но эти средства являются отличной базой для создания своих собственных библиотек, как, например, мы сделали для управления спрайтами. На этот раз у нас будет две программы, поскольку способов для вывода текста целых два.

И так, какие у нас есть варианты для отображения текста. На базе нашей библиотеки для управления спрайтами (а лучше второй версии) можно написать свою простую библиотеку для работы с текстом. Вместо кадров анимации мы будем загружать из изображения буквы. Примером такого изображения может служить вот это:

Осталось сделать механизм, который будет переводить строку char * в последовательность из спрайтов. Можно сделать последовательность символов в исходном рисунке в соответствии со стандартной кодовой таблицей (например ASCII или KOI8-R): номер кадра с символом равен соответствующему коду этого символа. Это самый простой пример создания своего механизма печати. Я пересмотрел несколько подобных механизмов, некоторые были гораздо изощреннее и богаче по возможностям. Возможно этот метод пригодится, когда нужно использовать уникальный шрифт в какой-нибудь игре. Теперь давайте попробуем сделать простую функцию для вывода текста с помощью растрового шрифта, т.е. шрифт, который находится в файле изображении.

Для начала познакомимся с библиотекой SDL_image . Достать ее можно на официальном сайте. Установка чрезвычайно проста. Для работы с этой библиотекой нам нужно подключить файл заголовков #include «SDL_image.h» и при линковке указать библиотеку: -lSDL_image . Эта библиотека очень простая в использовании. Справки с ней не прилагается, но в файле заголовков очень понятные комментарии.

Как я уже упоминал в предыдущих уроках, штатными средствами SDL мы можем загружать только файлы BMP. Но при помощи SDL_image мы можем работать с практически любым форматом изображения. Файл нашего растрового шрифта имеет формат PNG (Portable Network Graphics). Чтобы загрузить такой файл мы используем функцию IMG_Load из библиотеки SDL_image . Вот ее прототип: SDL_Surface* IMG_Load(const char* file);
И все! Таким нехитрым способом можно загрузить на поверхность SDL_Surface любую графику. И давайте посмотрим на функцию загрузки шрифта:

Функция очень простая. Она просто загружает изображение на поверхность и устанавливает прозрачность. Устанавливать прозрачность (Color Keying) мы уже умеем. Следует только добавить, что здесь у нас используется белый цвет в качестве прозрачного (триплет 255,255,255 ). Теперь об ограничениях нашего шрифта. Буквы могут быть только английские и только в верхнем регистре. Собственно, диапазон шрифта от A до Z . Цифр и других символов нет. Теперь немного теории о кодировках.

Как вы уже заметили, буквы в нашем шрифте идут по алфавитному порядку. И вот почему. В большинстве кодовых таблиц первые 128 символов занимают латинские буквы и стандартные и управляющие символы. Остальные 128 используются для локализации, то есть содержат символы других языков. Вот кусок из кодовой таблицы KOI8-R:

Нетрудно заметить, что латинские буквы построены по алфавиту, а вот русские нет. Это небольшая проблема для адаптации нашего примера для русского шрифта, хотя создать версию KOI8-R шрифта несложно. Поэтому мы рассмотрим только латинские буквы в верхнем регистре.

Далее код функции для рисования текста. Посмотрите на него внимательно, попытайтесь понять что происходит, а ниже мы разберем ее подробно:

В качестве аргументов передаем поверхность с загруженным изображением, координаты куда выводить текст и сам текст. Далее в цикле выводим по порядку каждый символ строки:
for (i = 0; i

Обязательно проверяем чтобы символ был в диапазоне A-Z:
if (str[i] >= ‘A’ && str[i]

Далее рассчитываем координату х в исходном изображении (откуда вырезать). У нас размеры буквы заведомо 32х32 .
Выражение (str[i] -‘A’) * 32; вычисляет эту координату. Допустим у нас символ B (код 66 ), тогда получаем: (66-65)*32 = 32 . То есть начинаем вырезать букву B в координатах x=32, y=0 . Для выходных координат ( в которые выводить текст) рассчитываем позицию каждого следующего символа: x + (i * 32); и поскольку индекс i самого первого символа равен нулю, то первый символ будет выведен в тех координатах, что поступают как аргументы.

И наконец, как же это добро использовать. Вот кусок кода, использующий наши функции:

Все чрезвычайно просто. Вы можете доработать эту функцию, расширив диапазон символов в шрифте. Например, можно создать шрифт на 256 символов, а вместо служебных и непечатных символов оставить пустое место. Код получается абсолютно несложным. Вы можете создавать шрифты любых форм, гарнитур и размеров. Посмотрите исходный код программы bmf.cxx (Bitmap Font) и попробуйте разобраться в нем. А теперь рассмотрим следующий способ.

Нет ничего лучше как использовать разнообразные и красивые TrueType шрифты, коих очень много можно найти в интернете и причем совершенно бесплатно. В системах Unix для этого существует библиотека FreeType, которая является бесплатной реализацией библиотеки для работы со шрифтами TrueType. В Win32 этот формат вообще является родным для системы. Но работа с библиотекой FreeType очень сложна. Разработчик библиотеки SDL Sam Lantinga был очень любезен и создал надстройку (wrapper) для библиотеки FreeType чтобы мы могли использовать ее в своих программах. Новая библиотека называется SDL_ttf. Найти ее можно все на том же официальном сайте SDL, который уже наверно стал местом вашего паломничества. Устанавливается эта библиотека чрезвычайно просто для любой платформы, под которую есть реализация SDL. Единственный заголовочный файл SDL_ttf.h помещается вместе с другими заголовочными файлами SDL . То же самое и для файла библиотеки ( libSDL_ttf.a и libSDL_ttf.so или SDL_ttf.lib и SDL_ttf.dll в Win32). Для работы с этой библиотекой вам нужно просто добавить заголовок: #include «SDL_ttf.h» и указать библиотеку для линковки: -lSDL_ttf . Вот и все подготовительные процедуры. Ну что ж, давайте попробуем наваять небольшой пример, чтобы убедиться, какая же простая эта библиотека.
Для начала необходимо инициализировать библиотеку SDL_ttf , наподобии того, как мы инициализируем SDL:

Также указываем вызывать функцию TTF_Quit() перед выходом из программы. Теперь посмотрите на код функции для вывода текста. Это очень просто.

  • SDL_Surface *sDest — поверхность, на которую будет скопирован текст;
  • char* message — текст сообщения;
  • char* font — имя файла TrueType шрифта (например arial.ttf );
  • int size — размер шрифта; SDL_Color color — структура, содержащяя цвет шрифта, вот ее прототип:

Параметр Uint8 unused не используется. И, наконец, SDL_Rect dest — содержит координаты, куда следует вывести текст (в этой структуре не используются элементы w и h ). Чтобы вывести текст, мы загружаем шрифт:
TTF_Font *fnt = TTF_OpenFont(font, size);

Далее создаем поверхность, на которую отображаем текст при помощи загруженного шрифта:
SDL_Surface *sText = TTF_RenderText_Blended( fnt, message, color);

Функция TTF_RenderText_Blended() отображает сглаженный шрифт. Если вам нужно использовать шрифт без сглаживания, то используйте TTF_RenderText_Solid() с теми же самыми параметрами. Наконец, копируем надпись на поверхность, которую задали в аргументах (можно задать к примеру screen ), удаляем временную поверхность sText , чтобы очистить память и закрываем шрифт.

Из main() вызываем функцию для отображения текста:

Сначала создаем структуры цвета и координат и заполняем их значениями, а затем выводим надпись на английском и на русском. Заметьте, что на русском можно писать, если только у вас шрифт в соответствующей кодировке. Прилагаемый с исходниками шрифт Courier в кодировке KOI8-R.
Рекомендую загрузить исходники для этого урока и внимательно их изучить.

Вывод текста в графическом режиме

В графическом режиме больше (по сравнению с текстовым режимом) возможностей для вывода текста. Можно указать шрифт, размер, ориентацию символов, способ выравнивания текста и т.п. (кириллица доступна не во всех стандартных шрифтах). Шрифт задается либо предопределенной константой, либо ее значением: Направление: HorizDir = 0 – вывод текста слева направо (горизонтальная надпись), VertDir = 1 – вывод текста снизу вверх (вертикальная надпись).

Размер определяет коэффициент увеличения символа. Для шрифта DefaultFont размер, равный 1, определяет исходную матрицу для представления символов (8х8 точек). Размер 2 приведет к выводу на экран образа символа, созданного на матрице 8х8, в область размером 16х16 точек и т.д. При больших размерах символы шрифта DefaultFont сильно искажаются, а качество векторных шрифтов остается приемлемым.

Пример. Установлен шрифт TriplexFont с размером символов 8х8 точек и горизонтальным направлением вывода.

OutТext(строка); – процедура; вывод строки от текущего положения курсора выбранным процедурой SetTextStyle шрифтом.
OutTextXY(X,Y,строка); – процедура; вывод строки от точки с координатами (X,Y) шрифтом, заданным процедурой SetTextStyle.

Пример. В результате выполнения этих процедур будет выведено слово Graphic в середине экрана.

Вывод чисел в графическом режиме.

Для вывода числа в графическом режиме необходимо преобразовать его в строку с помощью процедуры Str(Число, Строка); Параметр Число является выражением целого или вещественного типа, а выходной параметр Строка – строковое представление.

Пример. Фрагмент программы обеспечивает вывод на экран, начиная от точки с координатами (80,50), значения Xmax.

Информатика, программирование: Общая информация про MFC. Иерархия классов MFC. Место MFC в среде разработчика Visual C++, Контрольная работа

Общая информация про MFC. Иерархия классов MFC. Место MFC в среде разработчика Visual C++

Иерархия основных классов MFC. CObject (обеспечивает базовые операции ввода/вывода) →CGDIObject (поддержка графических объектов); CDC (класс, обеспечивающий связь с устройствами); CExeption (обработка исключительных ситуаций в MFC); CFile (базовый класс для обработки файлов); CMenu (поддержка обьектов меню); CCmdTarget (базовый для обработки сообщения)1)→CWnd (базовый класс для окон); СFrameWnd; 2)→CWinThread;CwinApp;

Структура простой MFC программы. Программа на MFC содержит, по крайней мере, 2 класса. И эти классы порождаются от CWnd и CWinThread. Для реализации простой программы сделаем следующую последовательность шагов: 1) Создать класс приложений, порожденный от CWinApp. 2) Создать класс окна, порожденный от CFrameWnd. 3) Для класса приложения объявить функцию InitInstance(). 4) В конструкторе класса окна вызвать функцию Create для создания окна. 5) Объявить глобальный объект приложения. 6) Создать карту сообщения. 7) Подключить заголовочные файлы и определиться с типом объектов.

class CApp: public CWinApp

Class CMainWin:public CFrameWnd

BEGIN_MESSAGE_MAP (CMainWin, CFrameWnd)

MFC — (Microsoft Foundation Class Library) базовая библиотека классов; Иерархия классов MFC. Библиотека MFC содержит большую иерархию классов, написанных на C++. В ее вершине находится класс CObject, который содержит различные функции, используемые во время выполнения программы и предназначенные, в частности, для предоставления информации о текущем типе во время выполнения, для диагностики, и для сериализации. Информация о типе времени выполнения. Если указатель или ссылка ссылается на объект, производный от класса CObject, то в этом случае предусмотрен механизм определения реального типа объекта с помощью макроса RUNTIME­ _CLASS(). Хотя в C++ имеется механизм RTTI, механизм, реализованный в MFC, намного более эффективен по производительности.

Диагностика. Каждый класс, производный от CObject, может по запросу проверить свое внутреннее состояние и выдать диагностическую информацию. Это интенсивно используется в MFC при отладке.

Сериализации. Сериализация — это механизм, позволяющий преобразовать текущее состояние объекта в последовательный поток байт, который обычно затем записывается на диск, и восстановить состояние объекта из последовательного потока, обычно при чтении с диска. Это позволяет сохранять текущее состояние приложения на диске, и восстанавливать его при последующем запуске.

Основные классы. Некоторые классы порождаются непосредственно от CObject. Наиболее широко используемыми среди них являются CCmdTarget, CFile, CDC, CGDIObject и CMenu. Класс CCmdTarget предназначен для обработки сообщений. Класс CFile предназначен для работы с файлами. Класс CDC обеспечивает поддержку контекстов устройств. Об контекстах устройств мы будем говорить несколько позднее. В этот класс включены практически все функции графики GDI. CGDIObject является базовым классом для различных DGI-объектов, таких как перья, кисти, шрифты и другие. Класс CMenu предназначен для манипуляций с меню. От класса CCmdTarget порождается очень важный класс CWnd. Он является базовым для создания всех типов окон, включая масштабируемые («обычные») и диалоговые, а также различные элементы управления. Наиболее широко используемым производным классом является CFrameWnd. Как Вы увидите в дальнейшем, в большинстве программ главное окно создается с помощью именно этого класса. От класса CCmdTarget, через класс CWinThread, порождается, наверное, единственный из наиболее важных классов, обращение к которому в MFC-программах происходит напрямую: С WinApp. Это один из фундаментальных классов, поскольку предназначен для создания самого приложения. В каждой программе имеется один и только один объект этого класса. Как только он будет создан, приложение начнет выполняться.

Функции-члены в MFC. Большинство функций, вызываемых в MFC-программе, являются членами одного из классов, определенных в библиотеке. Большинство функций API доступны через функции-члены MFC. Тем не менее, всегда можно обращаться к функциям API напрямую. Иногда это бывает необходимым, но все же в большинстве случаев удобнее использовать функции-члены MFC.

Глобальные функции в MFC. В библиотеке есть ряд глобальных функций. Все они начинаются с префикса Afx. (Когда MFC только разрабатывалась, то проект назывался AFX, Application Framework. После ряда существенных изменений AFX была переработана в MFC, но прежнее название сохранилось во многих идентификаторах библиотеки и в названиях файлов.) Например, очень часто используется функция AfxMessageBox(), отображающая заранее определенное окно сообщения. Но есть и член-функция MessageBox(). Таким образом, часто глобальные функции перекрываются функциями-членами. Файл AFXWIN.H. Все MFC-программы включают заголовочный файл AFXWIN.H. В нем, а также в различных вспомогательных файлах, содержатся описания классов, структур, переменных и других объектов MFC. Он автоматически подключает большинство заголовочных файлов, относящихся к MFC, в том числе и WINDOWS.H, в котором определены все функции Windows API и другие объекты, которые используются при традиционном программировании на С и «чистом» APL.

Каркас MFC-программы. В простейшем случае программа, написанная с помощью MFC, содержит два класса, порождаемые от классов иерархии библиотеки: класс, предназначенный для создания приложения, и класс, предназначенный для создания окна. Другими словами, для создания минимальной программы необходимо породить один класс от CWinApp, а другой — от CFrameWnd. Эти два класса обязательны для любой программы. Кроме создания вышеупомянутых классов, в программе также должна быть организована обработка всех сообщений, поступающих от Windows. В данном примере программа еще ничего полезного не делает, поэтому отвечать на каждое сообщение не нужно. MFC обработает все сообщения, которые нас не интересуют. Тем не менее в этом примере присутствует карта откликов на сообщения, или просто карта сообщений. Позже мы рассмотрим ее подробнее. Для создания стандартного окна в приложении должен наследоваться класс от CFrameWnd. В данном примере он называется CMainWin. Он содержит конструктор и макрос DECLARE_MESSAGE_MAP(). Макрос на самом деле разворачивается в декларацию карты сообщений, которая определяет, какая член-функция класса должна вызываться в ответ на сообщение Windows. Этот макрос применяется для любого окна, в котором обрабатываются сообщения. Он должен быть последним в декларации класса. Само окно создается в конструкторе с помощью вызова функции Create(). Эта функция используется почти во всех приложениях. Она выполняет действия по созданию окна. В этом примере приведен самый простой случай ее использования. Пока нам нужно знать, что второй параметр определяет заголовок окна, а первый чаще всего равен NULL.

Класс САрр приложения порождается от CWinApp. Этот класс отвечает за работу программы. В примере используется член-функция со следующим прототипом: virtual BOOL CWinApp::lnitlnstance(); Это виртуальная функция, которая вызывается каждый раз при запуске программы. В ней должны производиться все действия, связанные с инициализацией приложения. Функция должна возвращать TRUE при успешном завершении и FALSE в противном случае. В нашем случае, в функции сначала создается объект класса CMainWin, и указатель на него запоминается в переменной m_pMainWnd. Эта переменная является членом класса CWinThread. Она имеет тип CWnd* и используется почти во всех MFC-программах, потому что содержит указатель на главное окно. В последующих двух строчках через нее вызываются функции-члены окна. Когда окно создано, вызывается функция с прототипом: BOOL CWnd::ShowWindow(int How);

Обработка сообщений. Windows взаимодействует с приложением, посылая ему сообщения. Поэтому обработка сообщений является ядром всех приложений. В традиционных приложениях Windows (написанных с использованием только API) каждое сообщение передается в качестве аргументов оконной функции. Там обычно с помощью большого оператора switch определяется тип сообщения, извлекается информация и производятся нужные действия. Это громоздкая и чреватая ошибками процедура. С помощью MFC все делается намного проще. Здесь мы рассмотрим обработку в программе некоторых наиболее часто используемых сообщений.

Илон Маск рекомендует:  Пример отправки SMS сообщений на PHP

Обработка сообщений в MFC. В MFC включен набор предопределенных функций-обработчиков сообщений, которые можно использовать в программе. Если программа содержит такую функцию, то она будет вызываться всякий раз, когда поступает связанное с ней сообщение. При наличии дополнительной информации в сообщении она передается в качестве аргументов функции. Для организации обработки сообщений нужно выполнить следующие действия: В карту сообщений программы должна быть включена команда соответствующего сообщения. Прототип функции-обработчика должен быть включен в описание класса, ответственного за обработку данного сообщения. В программу должна быть включена реализация функции-обработчика.

2. Понятие контекста устройства, применение контекстов устройства, обработка сообщений WMHAR, WM_PAINT

В широком смысле контекст устройства является путем, по которыму Windows-программа, используя соответствующий драйвер, выводит информацию в рабочую область окна. Контекст устройства является структурой, которая полностью определяет состояние этого драйвера и способ вывода информации.

Прежде чем программа начнет выводить информацию в рабочую область окна, она должны получить контекст устройства. Пока это не сделано, нет связи между программой и окном, предназначенным для вывода.

В традиционных Windows-программах контекст устройства получают вызовом функции GetDC(), а освобождают с помощью ReleaseDC(). Поскольку Windows может предоставить лишь небольшое количество контекстов, важно, чтобы программа освободила контекст после окончания работы с ним. MFC имеет соответствующие классы, способные руководить этим процессом. В частности, при создании экземпляра объекта типа CClientDC программе предоставляется контекст устройства. Если этот объект необходимо изъять, вызывается функция ReleaseDC() и контекст устройства автоматически освобождается. Конструктор класса СClientDC записывается в виде:

СClientDC (CWnd *Windows);

где параметр Windows является указателем на окно, которому предоставляется контекст устройства. Для активного окна можно указать ключевое слово this.

Вывод в клиентскую область окна программы может обеспечиваться с помощью функции TextOut():

virtual BOOL CDC::TextOut(int X, int Y, LPCSTR lpszStr, int Length);

где X, Y – координаты точки начала вывода в пикселях (относительно левого верхнего угла окна программы), lpszStr – указатель на строку, которая выводится, а Length – его длина. Пример иллюстрирует возможный вариант реализации обработчика нажатия клавиш клавиатуры — в точку (1, 1) окна программы.

Пример обработчика нажатия клавиш клавиатуры

char str [80]; // строка символов для вывода

void CMainWin::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)

dc.TextOut(1,1,» «, 3); // удаление старого текста

wsprintf(str,»%с», ch); // формирование строки с кодом клавиши

dc.TextOut(1, 1, str, strlen(str)); // вывод строки в координату (1, 1)

Таким образом, каждый раз, когда необходимо вывести информацию в рабочую область окна, необходимо получить контекст устройства с помощью CClientDC. Частным случаем является применения сообщения WM_PAINT.

Обновление содержимого рабочей области окна программы

Для обновления содержимого рабочего окна программы необходимо отослать сообщение WM_PAINT. Вне его применения содержимое окна может не отображаться. Т.е. каждый раз, когда есть потребность выводить информацию в окно программы, следует обращаться к этому сообщению.

Сообщению WM_PAINT отвечает макрокоманда ON_WM_PAINT(), а макрокоманде – обработчик OnPaint(). Этот обработчик может выглядеть следующим образом:

Пример обработчика сообщения WM_PAINT

dc.TextOut(1,1, str, strlen(str)); // отображение символа

В примере приведен обработчик OnPaint(), который обеспечивает вывод на экран символа, введенного с клавиатуры пользователем соответственно обработчику OnChar(), записанному в предыдущем примере. Видно, что для получения контекста устройства здесь использован объект другого типа, а именно CPaintDC. В отличие от CClientDC, который работает только с клиентской частью окна программы, CPaintDC обеспечивает роботу со всей плоскостью окна.

В программе желательным было бы, чтобы Windows самостоятельно решала, когда ей вызвать сообщение WM_PAINT. Это так и происходит, например, когда пользователь программы минимизирует окно, максимизирует, движет экраном, изменяет размеры окна и т.п.. Но иногда необходимо проводить обновление окна принудительно. Для того, чтобы прислать сообщение WM_PAINT, программа вызывает функцию InvalidateRect() – член класса CWnd, которая имеет следующий прототип:

где lpRegion – указатель на область окна, которую необходимо обновить, Erase – флаг, который в значении TRUE устанавливает изъятие предыдущего содержимого окна. Если указать первому параметру значения NULL, произойдет полное обновление окна. Вызов функции InvalidateRect() обеспечивает принудительную посылку сообщения WM_PAINT и выполнение обработчика OnPaint().

Полное обновление окна, как например:

занимает много ресурсов системы, которая визуально может выглядеть мерцанием рабочего окна программы. Этого можно избегнуть, задав отдельно область обновления, например:

в таком случае будет обновляться лишь область, ограниченная прямоугольником с левым верхним углом (10,10) и нижним правым углом (100,100). Такой вариант обновления становится особенно интересным, если необходимо обеспечить подвижность отдельных элементов окна, а этого можно достичь с одновременным применением обработки сообщения таймера WM_TIMER.

3. Основы работы с текстом в MFC. Функции вывода текста, установки цветов, режимов отображения, получение метрик

В Windows есть практически неограниченные возможности по управлению выводом текста. Некоторые из функций сейчас утратили свое былое значение (или то, что было задумано, оказалось почти невозможным реализовать), другие же очень широко используются.

Любой шрифт, с которым мы имеем дело в Windows, характеризуется несколькими параметрами. Гарнитура (typeface) — это совокупность нескольких начертаний шрифта, объединенных стилевыми и другими признаками. Пример гарнитур: Arial, Times New Roman, MS Sans Serif. Размер шрифта — это высота прямоугольника, в который помещаются все символы шрифта, выражается в специальных единицах — пунктах. Пункт равен 1/72 части дюйма. Эта единица пришла из полиграфии. Начертание — это специфические характеристики шрифта. В Windows доступны четыре начертания: нормальное (normal), курсивное (italic), жирное (bold) и жирное курсивное (bold italic). Кроме того, шрифты могут быть моноширинные (fixed pitch‘, пример — Courier New, и пропорциональные (variable pitch), пример — Times New Roman.

Сейчас в Windows в основном используются шрифты двух групп: растровые (примеры — MS Sans Serif, Fixedsys) и контурные TrueType (примеры — Arial, Courier New). Первые представляют собой жестко определенные битовые матрицы для каждого символа и предназначены для отображения не очень крупного текста на экране. Вторые представляют собой очень сложные объекты. В них заданы контуры символов, которые закрашиваются по определенным правилам. Каждый шрифт TrueType — это программа на специальном языке, которая выполняется интерпретатором под названием растеризатор. Программа шрифта полностью определяет способ расчета конкретных битовых матриц символов на основе контуров. При использовании символов маленького размера (высотой приблизительно до 30 точек) модель контуров становится некорректной и символы сильно искажаются. Для борьбы с этим в качественных шрифтах используется разметка (хинты). Разметка шрифта -чрезвычайно сложный и долгий процесс, поэтому на рынке встречается немного качественно размеченных шрифтов. Поэтому использовать TrueType шрифты для вывода текста на экран нежелательно, за исключением стандартных шрифтов Windows (Times New Roman, Arial и Courier New), которые очень качественно размечены.

Координаты при выводе текста


За вывод текста в окно в MFC отвечает функция CDC::TextOut(). Ей нужно указать координаты для вывода строки. Эти координаты являются логическими координатами, то есть они измеряются в логических единицах. Это относится и к другим функциям вывода информации в окно. В процессе отображения информации логические координаты преобразуются в пиксели. По умолчанию логическими координатами являются пиксели, что для нас очень удобно.

Задание цвета текста и фона

При выводе текста с помощью функции TextOutQ по умолчанию текст выводится черным цветом на текущем (обычно белом) фоне. Однако с помощью следующих функций эти параметры можно изменить:

virtual COLORREF CDC::SetTextColor(COLORREF Color); virtual COLORREF CDC::SetBkColor(COLORREF Color);

Функции возвращают значение предыдущего цвета. Тип COLORREF представляет собой 32-разрядное беззнаковое целое число — представление цвета в виде красной, зеленой и синей компонент, каждая размером в 8 бит. Для формирования этого значения существует макрос RGB().

Задание режима отображения фона

С помощью функции SetBkMode() можно задать режим отображения фона. Прототип функции такой: int CDC::SetBkMode(int Mode);

Функция определяет, что происходит с текущим цветом фона (а также некоторых других элементов) при отображении текста. Режим может принимать одно из двух значений: OPAQUE и TRANSPARENT. В первом случае при выводе текста будет выводится также и текущий фон. Во втором случае фон выводится не будет (он будет «прозрачным»). По умолчанию используется режим OPAQUE.

Получение метрик текста

Большинство текстовых шрифтов являются пропорциональными, то есть ширина символов у них разная. Кроме того, расстояние между строками зависит как от шрифта, так и от его размера. Весь вывод текста в Windows осуществляется программно, поэтому необходимо учитывать все эти параметры.

Например, при выводе одной текстовой строки после другой предполагается, что известны высота шрифта и количество пикселей между строками. Получить эту информацию можно с помощью функции:

BOOL CDC::GetTextMetrics(LPTEXTMETRICS TextAtttrib) const; Параметр является указателем на структуру TEXTMETRIC, в которую будут записаны установки текущего шрифта контекста устройства. Структура имеет достаточно много полей. Наиболее важные поля следующие:

LONG tmHeight Полная высота шрифта

LONG tmAscent Высота над базовой линией

LONG tmDescent Высота подстрочных элементов

LONG tmlntemalLeading Пустое пространство над символами

LONG tmExternalLeading Пустой интервал между строками

LONG tmMaxCharWidth Максимальная ширина символов

Для получения числа логических единиц по вертикали между строками нужно сложить значения tmHeight и tmExternalLeading. Это не то же самое, что и высота символов.

Предположим, что мы имеем готовый инициализированный экземпляр класса CFont, содержащий некоторый шрифт (как это сделать, мы рассмотрим чуть ниже). Для изменения шрифта в контексте устройства используется член-функция SelectObject(), которая в нашем случае принимает один параметр — указатель на объект шрифта. После выполнения этой функции текст будет выводится новым шрифтом. Хотя функция возвращает указатель на объект старого шрифта, сохранять его вовсе не обязательно.

Инициализация объекта шрифта: выбор шрифта

После того, как объект класса CFont создан, необходимо инициализировать его конкретным шрифтом из установленных в системе, с заданными параметрами. Это может быть как растровый, так и контурный шрифт. Наверное, Вы справедливо ожидаете наличия функции, которая позволяет задать только гарнитуру шрифта, начертание и размер, после чего шрифт будет проинициализирован. К сожалению, ни в MFC ни в Windows такой функции нет. Единственная функция, которая подходит для выполнения этой задачи, имеет такой прототип:

BOOL CFont: :CreateFont(int nHeight, int nWidth,

Функция крайне неудобна, и смысл многих ее параметров на сегодняшний день не актуален.

Функция была создана давно, и тогда казалась увлекательной идея подстановки и замены шрифтов, суть которой заключалась в следующем: программист задает такие параметры шрифта, какие он хочет иметь, a Windows сама на основе имеющихся в системе шрифтов произведет необходимые трансформации и синтезирует требуемый шрифт из имеющихся. Впоследствии оказалось, что эта технология не может быть удовлетворительно работоспособной (так как практически это сложная задача из области искусственного интеллекта). Также функция позволяет проводить трансформации шрифта — растягивать и сжимать его, выводить текст под углом. Сейчас это используется редко. Графические пакеты используют свои алгоритмы трансформаций, и часто используют шрифты PostScript. Суть же заключается в том, что качественно могут быть отображены только те шрифты и в тех начертаниях, которые присутствуют в системе, причем без трансформаций (то есть с использованием хинтов, которые отключаются при трансформациях). Для большинства современных программ требуется высокое качество отображения текста, поэтому никакие трансформации и подстановки неуместны. Практически, являются разумными лишь два варианта поведения: либо программа будет использовать точно тот шрифт, который запросила, в таком виде, в каком его создал разработчик, либо откажется работать при отсутствии шрифта.

Рассмотрим пример кода, который позволяет задать размер шрифта, начертание и гарнитуру, минимизировав возможные трансформации шрифта и синтеза при отсутствии заданного. Такие же параметры для функции CreateFontQ Вы можете использовать в своих программах.

Желательно также проверять наличие шрифта, если он не является стандартным. Вот код, который используется в примере программы (указатель на объект шрифта хранится в переменной mjpFoni):

void CMainFrame::SetClientFont(CString Typeface, // Гарнитура

int Size, // размер в пунктах

BOOL Bold, // Признак жирного начертания

BOOL Italic // Признак наклонного начертания

// Получим контекст окна

// Узнаем, сколько пикселей в одном логическом дюйме У

int pixelsPerlnch = winDC.GetDeviceCaps(LOGPIXELSY);

// Узнаем высоту в пикселях шрифта размером Size пунктов

int fontHeight = -MulDiv(Size, pixelsPerlnch, 72);

// Устанавливаем параметр жирности для функции CreateFont()

int Weight = FW_NORMAL;

// Удаляем предыдущий экземпляр шрифта — нельзя дважды инициализировать шрифт вызовом CreateFont().

mjpFont = new CFont;

// Создание шрифта. Большинство параметров не используются.

m_pFont->CreateFont(fontHeight, 0, 0, 0, Weight, Italic, О, О,

DEFAULT_PITCH | FF_DONTCARE, Typeface);

1. Советов Б.Я., Яковлев С.А. “Робототехника”. — М.: Высш. шк., 2007.- 271 с.

2. Методические указания к курсовой работе по дисциплине «Робототехника и мехатроника» для студентов специальности ГКСР «. 2005.

Глава 8. Кисти, карандаши и многое другое.

Кисти, карандаши и многое другое.

В проблеске гениальности видишь свою бездарность

Станислав Ежи Леи

Начиная с этой главы, мы будем заниматься разработкой приложения, задача которого — отображать текущее время в виде аналоговых часов (т. е. часов с циферблатом и стрелками). Естественно, что основная цель преследуется несколько иная, а именно — показать использование большей части того, что предоставляет библиотека классов MFC в плане работы с графикой. По этой причине многие фрагменты кода написаны «не оптимально» — но это не должно вас смущать: благодаря такому подходу мы рассмотрим различные варианты решения одной и той же задачи (где-нибудь это вам обязательно пригодится).

Основа для нашего приложения у вас уже есть — это пример из главы 5, который, как вы помните, создавался именно для этой цели. Однако все-таки начнем по порядку. Класс приложения CClockApp пока трогать не будем, поскольку то, что создал мастер, нас вполне устраивает — связь с системой установлена.

Таким образом, у нас есть два класса — CMamFrame и CChildWnd . каждый из которых отвечает за свою часть работы приложения: первый — за общий вид, а второй за то, что пользователь будет видеть в окне.

Займемся сначала общим видом.

Изменение начального размера окна

Взгляните на следующий код, где полужирным шрифтом выделен фрагмент. который необходимо добавить к каркасу:

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

// Определяем размеры окна в зависимости от размеров экрана

int nSize = min (GetSystetriMetrics (SM_CXMAXIMIZED) ,

GetSystemMetrics(SM_CYMAXIMIZED)) * 3 / 5;

// Задаем горизонтальный и вертикальный размеры окна

cs.lpszName = «Приложение ‘Кварцевые часы'»;

// Устанавливаем свою пиктограмму

CS_HREDRAW | CS^HREDRAW | CS_DBLCLKS,

HBRUSH (COLOR_WINDOW + 1) ,

Как видите, сначала мы определяем ширину и высоту экрана, выбираем из них минимальное значение и устанавливаем размеры окна равными трем пятым этой величины.

Вот, собственно, пока и все, что необходимо добавить в код класса CMainFrame , поскольку никакого взаимодействия с пользователем в данном приложении не предполагается.

Для вывода надписей нам понадобятся шрифты, а для создания внешнего вида часов — кисти и карандаши. Чтобы не создавать их каждый раз при выводе рисунка (и, соответственно, не удалять после окончания вывода), сделаем это непосредственно в классе. В этом случае они будут освобождены при удалении объекта класса CChildWnd .

Зачем нам так много графических объектов — три шрифта и по пять кистей и карандашей — мы разберемся по ходу создания приложения, а пока запомним, что объекты Windows создаются в два этапа: сначала объект некоторого класса библиотеки MFC, а затем, вызовом соответствующей функции с префиксом Create , непосредственно объект Windows. Пока мы выполнили только первый этап — создали объекты соответствующих классов. По—•тому переходим ко второму этапу.

Создание графических объектов Windows

Как вы думаете, где лучше всего реализовать действия второго этапа по созданию графических объектов Windows? Надеюсь, что вы догадались. Действительно, в обработчике сообщения WM_CREATE — функции On Create . Поэтому первое, что надо сделать — это определить обработчик On Create. Как это сделать, мы уже рассматривав. После этого можно переходить непосредственно к созданию графических объектов Windows. И начнем мы со шрифтов.

Для работы со шрифтами в библиотеке MFC реализован класс СFont рис. 8.1), и для более ясного понимания дальнейших действий рассмотрим его несколько подробнее.

Рис. 8.1. Место класса CFont в иерархии библиотеки MFC

Класс CFont инкапсулирует графический объект Windows «шрифт». Для создания объекта этого класса имеется конструктор без параметров, однако чтобы создать собственно шрифт, необходимо вызвать одну из следующих рункций: CreateFont , CreateFontlndirect, CreatePointFont или CreatePointFontlndirect . Для использования созданного шрифта необходимо установить его в конкретный контекст устройства, а по завершении использования заменить ранее установленным шрифтом — все как и для других графических объектов.

Многие классы, инкапсулирующие графические объекты Windows, имеют в своем составе функции создания объектов при помощи структур. Именно с такого подхода мы и начнем. В данном случае это связано еще и с тем, что структура LOGFONT , используемая для создания шрифта, достаточно часто применяется не только вместе с классом CFont , но и, например, при выборе шрифта в блоке диалога, созданного при помощи класса CFontDialog .

Итак, для создания шрифта можно использовать функцию

BOOL CFont:: CreateFontlndirect (const LOGFONT* IpLogFont)

Параметр IpLogFont указывает на структуру LOGFONT, которая, в свою очередь, определяет шрифт, необходимый приложению.

Остановимся более подробно на составе структуры LOGFONT и описании ее полей.

Поле IfHeight определяет высоту (в логических единицах) знакомест или символов шрифта. Высота последних рассчитывается как разность между полной высотой знакоместа и размером области для отображения специальных знаков, например, ударения. Это поле структуры LOGFONT может интерпретироваться, в зависимости от знака, следующим образом:

Значение преобразуется в единицы устройства вывода и используется для выбора из имеющегося набора шрифта, соответствующего по высоте знакоместа

Для нахождения шрифта используется значение высоты по умолчанию

Значение преобразуется в единицы устройства вывода и используется для выбора из имеющегося набора шрифта, соответствующего по высоте символа

При поиске шрифта выбирается шрифт с наиболее подходящей высотой знакоместа или символа (высота должна быть меньше или равна запрашиваемой).

Для режима отображения ММ_ТЕХТ высота может быть определена через размер задаваемого в точках (points) шрифта следующим образом:

Режимы отображения мы рассмотрим несколько позже.

Поле IfWidth определяет среднюю ширину символов в логических единицах. Нулевое значение поля IfWidth обеспечивает выбор из имеющегося набора шрифта, наиболее подходящего к конкретному устройству по возможностям масштабирования.

Поле IfEscapement определяет угол (в десятых лолях градуса) между базовой линией выводимого текста и горизонталью

Поле IfOrientation определяет угол (в десятых долях градуса) между базовой линией каждого выводимого символа и горизонталью

К сожалению, в рамках Windows 95-98 значения обоих полей IfEscapement и IfOrientation должны быть равны, т. е. существует возможность вывода под наклоном только текста всей строки, когда базовые линии символов лежат на базовой линии строки. 3 Windows NT при установке графического режима GM_ADVANCED устанавливаемого функцией Л’т32 АР! ::SetGraphicsMode, возможности по выводу текста расширяются.

Поле IfWeight определяет толщину линий начертания символов и может принимать значения от 0 до 1000. При задании нуля используется значение по умолчанию. Некоторым значениям даны символьные наименования:

Поле Ifltalic определяет курсивное начертание шрифта.

Поле //Underline определяет подчеркнутое начертание шрифта.

Поле IfStrikeOut определяет перечеркнутое начертание шрифта.

Поле IfCharSet определяет таблицу кодировки шрифта. Определены следующие таблицы: ANSI_CHARSET. DEFAULT_CHARSET, SYMBOL_CHARSET, SHIFTJIS_ CHARSET, GB2312_CHARSET, HANGEUL_CHARSET, CHINESEBIG5_CHARSET. OEM_CHARSET.

В рамках Windows 95/98 дополнительно определены следующие таблицы: JOHAB_CHARSET, HEBREW_CHARSET, ARABIC_CHARSET, GREEK CHARSET, TURKISH_CHARSET, THAI_CHARSET, EASTEUROPE_CHARSET, RUSSIAN_CHAR-SET, MAC_CHARSET, BALTIC_CHARSET.

Значение OEM_CHARSET определяет таблицу кодировки конкретной версии операционной системы. Значение DEFAULT_CHARSET для идентификации шрифта требует задания имени (поле IfFaceName) и размера, однако отсутствие запрашиваемого шрифта может привести к непредсказуемым результатам, т. к. он будет подменен шрифтом, подходящим по другим параметрам, но неизвестной заранее кодировки.

При задании в явном виде имени шрифта особенно важно правильно задать таблицу кодировки, т. к. именно кодировка в первую очередь определяет набор установленных шрифтов, среди которых будет произведен поиск по имени (рис. 8.2).

Рис. 8.2. Пример доминирования заданной таблицы кодировки над именем шрифта

Поле IfOutPrecision определяет механизм выбора шрифта, т. е. насколько шрифт должен соответствовать заданным параметрам, прежде всего типу шрифта (растровый, системный или шрифт TrueType), а также ширине, высоте, ориентации и т. д. Это поле может принимать одно из следующих значений:

Определяет механизм выбора, принятый по умолчанию

Определяет, что при установке в системе нескольких шрифтов с одним и тем же названием выбирается системный шрифт

Только для Windows NT: выбор осуществляется из набора векторных или шрифтов TrueType

Определяет, что при установке в системе нескольких шрифтов с одним и тем же названием выбирается растровый шрифт

Используется для получения списка растровых шрифтов

Используется для получения списка векторных и шрифтов TrueType

Задает выбор только шрифтов TrueType, если ни один такой шрифт не установлен, используется механизм выбора, принятый по умолчанию

Определяет, что при установке в системе нескольких шрифтов с одним и тем же названием выбирается шрифт TrueType

Поле IfClipPrecision определяет механизм отсечения символов шрифта в случае, если символы частично оказываются вне области вывода. Это поле может принимать одно или несколько из следующих значений:

Определяет использование механизма, принятого по умолчанию

Используется только для получения списка растровых, векторных или шрифтов TrueType

Указывается при использовании внедренных шрифтов

Указывает, что отсчет направления угла поворота (по часовой стрелке или против) зависит от выбранной системы координат; если флаг не используется то поворот текста, выводимого с использованием системных шрифтов, производится против часовой стрелки, однако для всех остальных шрифтов направление поворота определяется системой координат

Поле IfQuality определяет желаемое качество вывода, которое зависит от выбранного шрифта. Определены следующие три значения качества вывода:

Качество вывода текста несущественн о


Качество вывода текста менее важно, чем для PROOF_QUALITY: для растровых шрифтов разрешено масштабирование; при необходимости возможно создание синтетических начертаний шрифтов — полужирного, курсивного, подчеркнутого и перечеркнутого

Качество вывода текста более важно, чем соответствие заданным атрибутам; для растровых шрифтов масштабирование запрещено, выбирается наиболее подходящий по размеру шрифт: при необходимости возможно создание синтетических начертаний шрифтов — полужирного, курсивного, подчеркнутого и перечеркнутого

Поле IfP/tchAndFamily определяет тип и семейство шрифтов. Это поле позволяет выбрать наиболее похожий на заданный по имени шрифт в случае, если последний не установлен в системе. Для задания типа и семейства шрифта используется операция OR (!) между определениями типа и семейства, соответственно

Илон Маск рекомендует:  Автоматическая обработка ошибок

Тип шрифта определяется двумя младшими битами поля и может принимать одно из следующих значений:

Непропорциональный шрифт, т. е. все символы шрифта имеют одинаковый размер

Семейство определяется старшими четырьмя битами поля и может принимать одно из следующих значений:

Декоративный шрифт, например, Old English

Семейство шрифта не имеет значения

Непропорциональный шрифт с засечками или без них, например, CourierNew*

Пропорциональный шрифт с засечками, например. MS* Serif

Шрифт, схожий по начертанию с рукописным, например, Script или Cursive

Пропорциональный шрифт без засечек, например, MS* Sans Serif

Поле IfFaceName содержит имя шрифта. Длина имени не может быть более 32 символов, включая завершающий нуль-символ. Если имя не указано или шрифт с таким именем не найден, поиск подходящего шрифта будет проводиться с использованием других заданных параметров. Однако в ряде случаев имя не является определяющим (см. комментарии к полю IfCharSet).

Таким образом, для создания шрифта, с помощью которого мы будем выводить название, нам необходимо заполнить поля структуры LOGFONT .

130, // Высота шрифта в логических единицах

0, //В качестве ширины символов

// выбирается значение, наиболее

// подходящее конкретному устройству

0, // Текст будем выводить

0, // не курсивный,

0, // не подчеркнутый

0, // и не перечеркнутый шрифт

DEFAULT_CHARSET, // Кодировка, установленная

OUT_DEFAULT_PRECIS, // Механизм соответствия заданным

// параметрам ло умолчанию

CLIP_DEFAULT_PRECIS, // Механизм отсечения,

// используемый по умолчанию

PROOF_QUALITY, // Важно качество шрифта

VARIABLE_PITCH | FF_DONTCARE, // Пропорциональный шрифт, а какое

// семейство — не имеет значения

«Arial» // Имя шрифта

После того как структура заполнена, для создания соответствующего объекта Windows осталось только вызвать функцию CreateFontIndirect , передав ей в качестве, параметра указатель на эту структуру.

Кроме этой функции, для создания шрифта можно воспользоваться функцией CreateFonr , которая принимает в качестве параметров те же значения, что и поля структуры LOGFONT .

Воспользуемся этой функцией для создания второго шрифта:

О, //В качестве ширины символов

// выбирается значение, наиболее

// подходящее конкретному устройству

О, // Текст будем выводить

О, //не подчеркнутый

О, // и не перечеркнутый шрифт

DEFAULT_CHARSET, // Кодировка, установленная

OUT_DEFAULT_PRECIS, // Механизм соответствия заданным

// параметрам по умолчанию

CLIP_DEFAULT_PRECIS, // Механизм отсечения,

// используемый по умолчанию

PROOFJ2UALITY, // Важно качество шрифта

VARIABLE_PITCH I FF_SWISS, // Пропорциональный шрифт

«Arial» // Имя шрифта

Как видите, особой разницы в рассмотренных способах создания шрифта нет. Однако в ряде случаев все же предпочтительнее пользоваться функцией CreateFontlndirect и вот почему.

Обратите внимание, что при создании двух шрифтов практически все их параметры совпадают. Конечно, это не обязательное условие, но в большинстве случаев именно так и бывает. Поэтому, чтобы не повторять каждый раз описание всех параметров, можно просто заменить значения в соответствующих полях уже созданной структуры LOGFONT и вызвать функцию CreatFontlndirect . Как вы помните, нам надо создать еще один, последний, шрифт. Воспользуемся для этого предложенным методом:

// Делаем шрифт самым «тонким»

1f.lfWei ght = FW_THIN;

// Устанавливаем пропорциональный шрифт без засечек

If.IfPinchAndFamily = VARIABLE_PITCH [ FF_SWISS;

Осталось только вызвать соответствующую функцию, что мы и делаем:

Большой разницы в рассмотренных двух способах нет — какой вам больше понравился, таким и пользуйтесь. Мне же осталось рассказать еще о двух функциях.

В ряде случаев при выборе шрифта нет необходимости столь подробно указывать его ожидаемые параметры. В то же время использование большинства текстовых редакторов приучает к тому, что размер шрифта характеризуется вполне точным, если хотите, абсолютным значением в пунктах ( points ). А пункт — это вовсе не экранная точка, т. е. пиксел, а единица длины, равная 1/72 дюйма. Класс СFont имеет в своем составе функцию CreatePointFont , которая, с одной стороны, создает шрифт на основе только его имени, а с другой — устанавливает желаемый размер как раз в привычных всем единицах:

Первый параметр nPointSize определяет величину шрифта в десятых долях пункта, например, при выборе шрифта с размером символов в 8 пунктов необходимо установить значение этого параметра в 80,

Имя шрифта задается параметром IpszFaceName, при задании нуля выбирается системно-независимый шрифт.

Последний параметр pDC нужен для правильного преобразования размера шрифта в логические единицы, которые определяются именно для контекста устройства. Нулевое значение параметра приводит к выбору в качестве контекста устройства контекст экрана и, соответственно, его логических единиц.

Аналог CreateFontIndirect — функция CreatePointFontIndirect — в качестве параметра также принимает указатель на структуру LOGFONT . однако единицами измерения в поле lfHeight этой структуры являются пункты.

Мы не воспользовались этими функциями в частности еще и потому, что у нас пока не определен контекст устройства.

Итак, мы подготовили шрифты и переходим к следующему необходимому нам объекту — кисти.

Процесс создания кисти значительно проще, чем создание шрифта. В библиотеке MFC реализован класс CBrush (рис. 8.3). который инкапсулирует графический объект Windows «кисть» для использования в качестве основы при закрашивании замкнутых фигур. Кисти могут быть однотонные, со штриховкой или созданные на основе шаблона.

Как и все другие классы графических объектов, класс CBrush имеет в своем составе конструктор без параметров — CBrush::CBrush() , который создает объект класса, но для его использования требует вызова одной из «создающих» функций — CreateSolidBrush, Create Hatch Brush, CreateBrush Indirect , CreatePatternBrush, CreateDIBPatternBrush, CreateSysColorBrush. Первые четыре функции полностью дублируют возможности конструкторов. Пятая функция создает кисть на основе битового массива, но, в отличие от четвертой, этот массив хранит изображение в независимом от устройств виде. Последняя функция создает кисть со штриховкой на основе системного цвета. Рассмотрим некоторые из этих функций более подробно.

Рис. 8.3. Место класса CBrush в иерархии библиотеки MFC

BOOL CBrush:: CreateSolidBrush(COLORREF crColor)

Используется для создания однотонной кисти, цвет которой задается параметром crColor. Тип COLORREF определяет 32-разрядное значение, задающее цвет в системе RGB (красный, зеленый, синий) и имеющее формат OxOObbggrr.

Для задания параметра crColor можно воспользоваться определенным в Win32 API макросом RGB, который комбинирует интенсивности (в диапазоне от 0 до 255) трех цветов — красного, зеленого и синего и определяется следующим образом:

COLORREF RGB(int red, int green, int blue)

Итак, для создания однотонной кисти, которой мы будем закрашивать фон наших часов, нам необходимо задать ее цвет:

fdefine COLOR_BCKGR RGB(224, 224, 224)

и вызвать функцию CreateSolidBrush :

Аналогичную операцию проделаем и для других кистей, за исключением одной:

#define COLOR_ROUND RGB (64., 128, 128)

#define COLOR_CRNER RGB(64, 128, 128)

m_clkBrush.CreateSolidBrush(COLOR_CLOCK); m_rndBrush.CreateSolidBrush(COLOR_ROUND); m_crnBrush.CreateSolidBrush(COLOR_CRMER);

Для фона стрелок наших часов используем кисть со штриховкой. Создать ее так же просто, как и однотонную кисть:

Здесь параметр nlndex определяет один из возможных вариантов штриховки:

Штриховка слева направо под углом 45° и сверху вниз

Горизонтальная и вертикальная штриховка крест накрест

Штриховка крест накрест под углом 45°

Штриховка слева направо под углом 45° и снизу вверх

a crColor— значение цвета линий штриховки.

Вот как это будет в нашем случае:

HS_DIAGCROSS, // Штриховка крест накрест под углом 45°

CCLOR_ARROW) //цвет линий штриховки

Кисть для фона реализуем с помощью специатьного шаблона. В этом случае необходимо сначала создать или загрузить битовый массив, а затем вызвать функцию

CBrush:: CreatePatternBrush (CBitmар* pBimap)

В качестве параметра pBitmap функции передается указатель на объект класса CBitmap. представляющего в библиотеке объект Windows «битовый массив».

При использовании этой функции надо иметь в виду, что размеры битового массива должны быть не меньше чем 8×8 пикселов, а сама кисть создается на основе левой верхней квадратной области битового массива размером также 8×8 пикселов.

Я думаю, вам теперь не составит труда самостоятельно разобраться с остальными имеющимися в классе CBrush функциями, а нам надо прерваться, чтобы познакомиться с классом CBitmap .

Создание битовых массивов

Для работы с битовыми массивами в библиотеке MFC реализован класс CBitmap (рис. 8.4), который инкапсулирует графический объект Windows «битовый массив».

Рис. 8.4. Место класса CBitmap в иерархии библиотеки MFC

Как обычно, для работы с объектом класса необходимо создать сам объект, а затем создать собственно битовый массив Windows путем вызова одной из функций CreateBitmap, Create Bitmap Indirect, CreateCompatibleBitmap или Create Discardable Bitmap , загрузить его из соответствующего ресурса (функции LoadBitmap, LoadOEMBitmap, Load Mapped Bitmap) или установить связь с ранее созданным (загруженным) битовым массивом (функция Attach ). Кроме этого, класс предоставляет набор функций, позволяющих менять и узнавать содержимое (SetBitmapBits и GetBitmapBits) и размеры ( SetBitmap Dimension и GetBitmapDimension ) массива.

Для нашего примера мы воспользуемся функцией загрузки битового массива, созданного в качестве ресурса:

BOOL CBitmap:: LoadBitanap (LPCTSTR’ дpszResourceName)

BOOL CBitmap:: LoadBitmap (UINT nIDResource)

Функции позволяют загрузить битовые массивы, заданные текстовой строкой (параметр IpszResourceName ), т. е. по имени, или числовым идентификатором (параметр nIDResource ). Загрузка битовых массивов может осуществляться прямо из ресурсов исполняемого модуля текущего приложения.

Очевидно, что сначала требуется создать соответствующий ресурс. Вот как он будет выглядеть у нас (рис. 8.5).

Затем ресурс надо загрузить в объект класса CBitmap :

Рис. 8.5. На основе этого шаблона мы будем создавать «цвет фона» кисти часов

Конечно же. в классе СBitmap есть и другие функции, но рассмотренный подход — один из наиболее простых, и поэтому мы пока им и ограничимся — пора возвращаться к нашему примеру.

После того как объект класса СBitmap создан, нам осталось передать указатель на него в функцию CreatePatternBrush :

Результатом наших действии будет кисть на основе этого шаблона.

Теперь, когда мы научились создавать не только шрифты и кисти, но битовые массивы, перейдем к последнему (используемому нами, но не последнему из имеющихся) объекту, а именно, к карандашам.

Класс СРеn инкапсулирует графический объект Windows «карандаш» (рис. 8.6). Карандаши используются для рисования различных линий и контуров объектов. Эти объекты имеют наиболее богатые возможности для их создания — в Windows имеется целый набор различных предопределенных стилей, свойств и типов, которые позволяют использовать карандаши просто и эффективно и, соответственно, проводить с их помощью самые разнообразные линии.

Рис. 8.6. Место класса СРеn в иерархии библиотеки MFC

Как и все другие классы графических объектов, класс СРеn допускает как создание объектов класса, не связывая их при этом с объектами Windows, так и одновременное создание объекта класса и соответствующего графического объекта.

Что касается последней возможности, то она дублируется наличием функций создания карандаша Windows уже после создания объекта класса. Поскольку мы уже создали объекты этого класса, то не будем рассматривать имеющиеся в классе конструкторы, а подробно остановимся только на специальных функциях создания соответствующих объектов Windows.

const LOGBRUSH* pLogBrush,

int nStyleCount = 0,


const DWORD* IpStyle-= NULL)

Давайте рассмотрим отдельно для каждой версии функции CreatePen возможные значения параметра nPenStyle .

В результате использования первой версии функции создаются карандаши с предопределенными в Windows стилями для рисования следующих типов линий:

Длинные штрихи, толщина которых равна 1

Короткие штрихи, толщина которых равна 1

Штрихпунктир, толщина которого равна 1

Двойной штрихпунктир, толщина которого равна 1

Сплошная линия для рисования по внутренней границе замкнутой области, например, при использовании функций класса CDC — Rectangle, RoundRect, Ellipse, Chord и т. д.; при использовании карандаша данного типа для рисования линий, например, при помощи функции LineTo, результат будет тем же. что и для стиля PS_SOLID. Еще одна область применения этого стиля — создание карандашей толщиной более одного пиксела и имеющих цвет, недоступный для текущего устройства. В этом случае линии будут состоять из пикселов доступных цветов, совокупность которых наиболее близко соответствует заданному цвету

Для наглядности продемонстрируем линии различных стилей. Обратите внимание, что при выводе линий одной длины некоторые штриховые линии фактически оканчиваются раньше сплошных (рис. 8.7), что вполне естественно — эти линии завершились в месте пустого пространства между штрихами. Такой эффект может негативно сказаться при решении конкретных задач.

Рис. 8.7. Различные стили линий

Использование второй версии функции CreatePen позволяет комбинировать в параметре nPenStyle стиль, тип, режим рисования концов линии и режим соединения линий. Каждый атрибут рисования может быть представлен только одним значением, заданные значения комбинируются при помощи оператора OR ([).

Карандаши Windows бывают двух типов — PS_GEOMETRIC и PS_COSMETIC , отличающихся использованием кистей для рисования линий и требованиями к заданию ширины. Кроме того, от выбранного типа зависит интерпретация параметров, задающих размеры штрихов для пользовательских стилей.

Вторая версия функции CreatePen поддерживает два дополнительных стиля — PS_ALTERNATE (может использоваться только совместно с типом PS_COSMETIC ) и PS_USERSTYLE , который для рисования линий требует задания массива длин штрихов.

Для карандашей также возможно задание режима рисования концов (естественно, это имеет смысл при рисовании широких линий). Таких режимов три, для первых двух фактическая длина линии может быть больше заданной:

Концы линий имеют прямоугольную форму

To же, что и для PS_ENCAP_SQUARE, но концы линии не выходят за заданные размеры

Для соединения линий можно выбирать один из следующих режимов:

Соединение определяется продолжением контура линий, если только расстояние между концами линий не превышает заданную функцией CDC::SetMHerLimit величину, в противном’случае соединение обрезается, как и в случае задания режима PS_JOIN_BEVEL

Примеры различных типов, стилей рисования и соединения линий представлены на рис. 8.8. Одно из требований при применении режима рисования концов линий и соединений заключается в том, что рисование линий при помощи карандашей, созданных с флагами PS_ENDCAP_SQUARE, PS_ENDCAP_FLAT. PS_JOIN_.BEVEL и PS JOIN_MITER , можно осуществлять только при помощи механизма использования контуров. Обратите внимание, что для наглядности по оси каждой линии, имеющей тип PS_GEOMETRIC , проведены сплошные тонкие линии с теми же координатами.

При создании карандаша необходимо также задать его ширину с помощью параметра nWidth . В первой версии функции CreatePen заданием ширины, равной 0, гарантируется вывод линии шириной в один пиксел независимо от режима вывода ( mapping mode) . При использовании второй версии функции ширина задается в логических единицах — для карандашей типа PS_GEOMETRIC , а для карандашей типа PS_COSMETIC ширина должна быть равна 1.

Для задания цвета карандаша используется параметр crColor .

Рис. 8.8. Различные стили рисования и соединения линий

Параметр pLogBrush является указателем на структуру LOGBRUSH для создания кисти, с помощью которой будет производиться рисование линий. Для карандашей типа PS_COSMETIC элемент структуры LOGBRUSH::lbStyle должен быть равен BS_SOLID .

Параметр IpStyle указывает на массив, содержащий nStyleCount чисел, определяющих попарно длину штриха и расстояние между штрихами для карандашей, имеющих стиль PS_USERSTYLE .

Кроме рассмотренных, для создания карандашей могут быть использованы функции CreatePenlndirect или CreateStockObject . При помощи первой из них возможно создание только обычных карандашей, т. е. таких же, какие можно создавать при помощи первой версии функции CreatePen . Как и в случае создания кистей, никаких сложностей при создании карандашей нет. Вот как мы осуществляем это в нашем примере:

#define COLOR_BCKGR RGB(224, 224, 224)

#define COLOR_BLACK RGB( 0, 0, 0) •

#define COLOR_ARROW RGB( 0, 128, 128)

#define COLOR_CRNER RGB( 64, 128, 128)

m_wndPen.CreatePen(PS^SOLID, 1, COLOR_BCKGR); m_arrPen.CreatePen(PSJSOLID, 1, COLOR_ARROW) ;

Единственное исключение мы сделаем для карандаша, которым будем рисовать «метки» для обозначения цифр. В этом случае используем вторую версию функции CreatePen . Для нее, как вы помните, нам необходим указатель на структуру LOGBRUSH . которая содержит параметры кисти, применяемой при рисовании «толстых» линии. Посмотрите, как можно получить параметры кисти, используемой в качестве основы карандаша:

После того как поля структуры заполнены, нам осталось только вызвать функцию создания карандаша, передав ей в качестве параметра указатель на эту структуру:

PS_SOLID I PS_3EOKETRIC [ // «Сплошные»,

PS_ENDCAP_SQUARE, // с прямоугольными концами линии

20, // Ширина 20 пикселов

Задание прямоугольных концов для линий, для которых создаем карандаш, в нашем примере не влияет на рисование, т. к. это свойство проявляется, когда рисование осуществляется при помощи создания контуров, а данный карандаш будет использоваться для рисования дуг, которые функцией CDC::StrokePath не отображаются.

Здесь мне хотелось бы рассмотреть еще один графический объект, который мы пока не создавали, но который понадобится нам при рассмотрении вопросов рисования. Речь идет о регионах.

Работа с регионами

Класс CAgn инкапсулирует объект Windows «регион», представляющий собой совокупность прямоугольных и эллиптических областей (рис. 8.9).

Рис. 8.9. Место класса СRgn в иерархии классов библиотеки MFC

Основное назначение регионов — определение области отсечения в пределах окна. Класс CRgn имеет в своем составе набор функций для создания регионов, схожих по своим параметрам с функциями рисования прямоугольников, эллипсов и многоугольников, а также функции, позволяющие комбинировать регионы.

Для создания региона при помощи класса CRgn необходимо использовать конструктор без параметров, который создает объект класса CRgn . но не создает при этом собственно объект Windows. Для создания последнего можно воспользоваться функциями, описанными ниже.

Создание прямоугольных регионов

BOOL CRgn:: CreateRectRgnlndirect (LPCRECT IpRect)

Первая версия функции получает в качестве параметров координаты левого верхнего и правого нижнего углов прямоугольной области. Вторая версия функции получает указатель на структуру RECT, в которой указываются также координаты прямоугольной области.

Создание эллиптических регионов

BOOL CRgn:: CreateEllipticRgnlndirect (LPCRECT IpRect)

Обе версии функции получают информацию, аналогично версиям предыдущей функции, о прямоугольной области, в которую вписан эллипс.

Создание сложных регионов на базе многоугольников

Функция принимает в качестве информации о многоугольнике массив координат (IpPoints) и количество его вершин (nCount).

BOOL СRgn:: CreatePolyPolygonRgn (

Функция принимает в качестве информации массив координат вершин многоугольников (IpPoints] и массив, содержащий количество вершин для каждого из них (IpPolyCounts). Параметр nCount задает количество многоугольников.

Параметр nPolyFilIMode в этих функциях определяет режим закраски ALTERNATE или WINDING (см. следующую главу).

Создание регионов с закругленными углами

Первые четыре параметра определяют прямоугольную область, а параметры хЗ (л уЗ определяют ширину и высоту эллипса, на основе которого строятся закругленные углы.

Более подробно остановимся на функции, которая на основе комбинации двух регионов изменяет третий. Именно изменяет, т. к. объект класса, для которого вызывается эта функция и который, собственно, и будет хранить результат комбинации двух других регионов, обязательно должен существовать — так работает Windows, так работает и библиотека классов MFC.

Объект класса CRgn , для которого вызывается функция, может совпадать с одним из тех, указатели на которые передаются в качестве параметров.

Функция возвращает одно из следующих значений, показывающих, какого типа регион получился в результате комбинирования:

Илон Маск рекомендует:  Puts вывод строки в файл

Регион состоит из набора более простых областей

Регион представляет собой одну простую область — прямоугольник или эллипс

Параметры pRgnl и рRgn2 являются указателями на объекты класса СПдп, которые должны быть связаны с регионами Windows, именно они и будут комбинироваться. Параметр nCombineMode определяет режим комбинирования. Таких режимов несколько:

Регион будет являться пересечением обоих заданных, т. е. определяться областью, которая относится и к первому, и ко второму региону одновременно

Регион будет точной копией первого региона (pRgnl)

Регион будет включать в себя только те области первого региона (pRgnl), которые не пересекаются со вторым (pRgn2)

Регион будет являться объединением обоих заданных, т. е. будет включать все области, относящиеся и к первому, и ко второму региону

Регион будет включать области первого региона (pRgnl) и второго региона (pRgn2), кроме тех, которые принадлежат и первому и второму одновременно

Несмотря на то, что Windows для осуществления комбинирования требует наличия объектов для всех трех регионов, возможно использование в качестве одного или даже обоих параметров указателя на объект класса CRgn , для которого и вызывается функция CombineRgn :

Кроме вышеперечисленных функций, упомянем еще несколько. Класс CRgn имеет в своем составе функцию копирования CopyRgn , функцию преобразования текущего контура ( path ) в регион ( CreateFromPath ), функцию сравнения двух регионов ( EqualRgn ).

Для иллюстрации использования регионов приведем процедуру, в которой в результате комбинирования трех регионов производится закрашивание части замкнутых областей одним вызовом функции рисования (рис. 8.10).

// Определяем треугольную область с одной вершиной

//в центре клиентской части окна и двумя другими,

// совпадающими с левым верхним и левым нижним углами

rect.right / 2, rect.bottom / 2,

rgn_l.CreatePolygonRgn;pt__l, 4, WINDING);

// Определяем треугольную область с одной вершиной

// в центре клиентской части окна и двумя другими,

// совпадающими с правым верхним и правым нижним углами

rect.right / 2, rect.bottom / 2,

rgn_2.CreatePoiygonRgr, (pt_2, 4, WINDING);

// Определяем прямоугольную область, лежащую точно

//в центре клиентской области окна и немного меньше ее

rgn.CreateRectRgn/40, 40, rect.right-40, rect.cottom-40);

// Определяем регион как комбинацию трех заданных

// Определяем параметры рисования

CBrush brush(HS_DIAGCROSS, RGB(0, 0, 0));

CBrush «pBrush = dc.SelectObnect(Sbrush);

dc.SetBkColor(RGB(123, 128, 128));

// область отсечения dc.SelectObject(&rgn);

// Рисуем прямоугольник на всю клиентскую часть окна

Рис. 8.10. Результат работы функции CRegionFrame::OnPaint

К сожалению, даже на таком простом примере при выполнении программы видно, как медленно происходит рисование: такое впечатление, что для каждой точки проверяется возможность ее закрашивания.

На этом подготовительные работы по созданию необходимых нам графических объектов закончены, и мы переходим к следующему вопросу.

Изменение размеров окна

Вообще-то за изменение размеров окна в приложениях отвечает класс фрейма (у нас это CMainFrame ). И более того, для поддержки этой возможности нам не пришлось писать ни строчки кода — все сделала библиотека MFC. Нас же вопрос изменения размеров интересует несколько с другой точки зрения: мы хотим, чтобы размер наших часов всегда совпадал с размерами окна.

Реализовать это чрезвычайно просто. Прежде всего необходимо определить две переменные, которые будут хранить текущие значения ширины и высоты окна:

int m_cxClient; // ширина клиентской области окна

int m_cyClient; // высота клиентской области окна

А для отслеживания текущих значений ширины и высоты необходимо определить обработчик сообщения WM_SIZE . Вот как он будет выглядеть:

void CChildWnd: :OnSize (UINT nType, int ex, int. cy)

// He вмешиваемся в работу библиотеки

CWnd::OnSize(nType, ex, су);

// Запоминаем размер клиентской области

// окна, чтобы каждый раз при перерисовке

// не запрашивать размер заново

Вот собственно и все. Теперь, как только нам понадобятся параметры размеров часов, мы всегда можем получить их текущие значения.

Обработка сообщения WM_PAINT

Многие из вас, конечно же, ожидали, что в конце концов мы придем к созданию обработчика сообщения WM_PAINT . И вы не ошиблись, хотя в нашем конкретном случае вполне можно было бы обойтись и без него. Дело в том, что рисование часов обладает особенностью — их надо перерисовывать не в зависимости от «поврежденности» окна, а тогда, когда истечет выбранный нами отрезок времени (через секунду, если будем рисовать секундную стрелку, или через минуту, если секундную стрелку рисовать не будем).

Однако для демонстрации «работы» этого наиболее часто используемого обработчика я решил определить в нем перерисовку фона. Вот как выглядит обработчик OnPaint в нашем примере:

CPaintDC dc(this); // device context for painting

// контекст устройства для перерисовки

// Закрашиваем клиентскую область,

// для чего создаем однородную кисть и .

dc.FillRect (CRect (0, 0, m_c;

// Циферблат, текст и стрелки рисовать не будем,


// этим займется функция CClockApp::OnIdle

// Do not call CWnd:: OnPaint () for painting messages

// Это едва ли не единственное место,

// где не надо вызывать переопределенную функцию базового класса

Английский текст в обработчике OnPaint вставляется мастером ClassWizard .

Вы наверняка обратили внимание на вызов функции FillRect , которая занимается закрашиванием прямоугольника размером с клиентскую область окна. И уж точно догадались, что она принадлежит одному из классов, отвечающих за контекст устройства. И это действительно так. Поэтому здесь мне бы хотелось прервать самого себя и рассмотреть более подробно класс CDC , поскольку ни одно действие по выводу чего-либо в окно (рисованию в окне) не обходится без участия этого класса (прямо или опосредованно).

Однако задержимся еще ненадолго, чтобы закончить со всеми остальными, отличными от рисования, вопросами.

Организация процесса рисования

Ясно, что нам нужна некоторая функция, которая будет заниматься собственно рисованием в окне. Назовем ее, например, Redraw (нам же периодически придется заниматься именно перерисовкой). Очевидно, что она должна принадлежать классу CChildWnd , отвечающему за окно, в котором мы будем рисовать.

Теперь необходимо определить, где и когда следует вызывать эту функцию. Первое, что приходит на ум: создать таймер и с заданной периодичностью вызывать Redraw. Безусловно, это один из вариантов. Однако я предлагаю рассмотреть другой способ, а работу с таймером мы разберем при обсуждении строки состояния.

Вернемся немного назад — к классу CWinApp, который мы рассматривали в главе 3. Как вы помните, на базе этого класса создается объект приложения. Некоторые основные его функции мы рассмотрели, нас же сейчас интересует еще одна, а именно:

virtual BOOL CWinApp::OnIdle (LONG ICount)

Параметр ICount увеличивается перед каждым вызовом функции, т. е. всякий раз, когда очередь сообщений приложения пуста, и сбрасывается в 0 при обработке нового сообщения. Этот параметр можно использовать для определения различных режимов обработки или относительного времени «простоя» приложения.

Функция CWinApp::OnIdle вызывается в цикле обработки сообщений, когда очередь сообщений приложения пуста. Функция, выполняемая по умолчанию, обновляет команды объектов интерфейса пользователя, такие как элементы меню и кнопки панелей инструментов, и очищает внутренние структуры данных. Если вы переопределяете эту функцию, то в новой версии необходимо сначала вызвать базовую функцию CWinApp::Onldle с параметром ICount .

Именно этой функцией мы и воспользуемся (естественно, сначала ее переопределив). Рассмотрим необходимые действия:

1. Откройте Class Wizard и в раскрывающемся списке Class name выберите класс нашего приложения — CClockApp . В окне Messages найдите и выделите виртуальную функцию Onldle (рис. 8.11).

2. Нажмите последовательно кнопки Add Function и Edit Code и в созданную заготовку внесите следующий код:

// чтобы не закончить цикл вызова, функции Cr.Idle возвращаем TRUE

Рис. 8.11. Переопределяем виртуальную функцию Onldle

3. В классе CMainFrame определяем функцию Redraw :

class CMainFrame : public CFraraeWrid

// Функция перерисовки часов может вызываться

// практически в любой функции программы

//У нас это будет происходить во время «простоя» приложения

void CMainFrame: :Redraw ()

4. В классе CChildWnd определяем свою функцию Redraw :

// Именно здесь будем реализовывать рисование часов

Вот как будет выглядеть окно приложения после того, как мы реализуем функции отображения часов (рис. 8.12).

И теперь мы спокойно можем переходить к знакомству с одним из основополагающих классов библиотеки MFC, а именно, классом CDC.

Рис. 8.12. Окно приложения «Аналоговые часы» при начальных размерах окна

XNA: Вывод текста системными шрифтами

XNA предполагает вывод текста только заранее подготовленными растровыми шрифтами. И это правильно. Быстро, не зависимо от ОС, предсказуемые размеры текста.
В моём случае требовалось совершенно противоположное. Произвольный выбор гарнитуры и размера шрифта, и низкие требования к производительности. Задача оказалась довольно трудной. Информации в интернете оказалось мало и она была крайне разрознена.

Условия: 2D приложение, пользователь в любое время должен иметь возможность изменить гарнитуру, стиль и размер шрифта.

Способ решения довольно прост. Создаём битмап с нужным текстом, создаём из него текстуру и отображаем её.

Битмап создаём средствами GDI.

Преобразуем битмап в текстуру

И отображаем текстуру

В результате получаем текст с рваными краями, полное отсутствие сглаживания.

Конвертация битмапа в текстуру оказалась нетривиальной процедурой.
Метод найден здесь

Для корректной передачи альфа-канала требуется произвести дополнительные действия в методе TextureFromBitmap.
Сначала отрисовываем в текстуру данные о цветах битмапа, потом информацию об альфа-канале.

Теперь получаем удовлетворительный результат.

Проблемы:
1. Медленно. Ни в коем случае не надо использовать этот метод в играх и тем более на мобильных устройствах.
2. Не надежно. Нужных шрифтов может не оказаться в системе.
3. Желательно отслеживать какая часть текста будет отображаться на экране и обрезать невидимый текст. Максимальный размер текстуры 2048×2048. Если размер битмапа будет больше, то текстура будет создана максимального размера и потом растянута средствами видеокарты до нужного размера. Текст будет размытым.

Можно избавиться от переконвертирования в PNG в TextureFromBitmap используя неуправляемый код. Пример можно посмотреть здесь.

Вывод текста и шрифты mfc

При установке некоторых шрифтов текст выводится несколько левее, чем должен быть. У меня в программе посимвольный вывод с использованием ::TextOut. Я хочу, например, вывести букву «f» в позицию 0,0, используя шрифт

Символ выведется немного левее (то есть немного за рамкой). При смещении позиции на ширину символа, полученную GetExtentPoint32() это «обратное смещение» также не учитывается. Что делать чтобы такого смещения не было или узнать его. Я пробовал SetTextAlign, не помогло. GetTextMetrix, там есть поле lfOverhead, но оно оказалось равным 0. Использование DrawText() тоже не помогает.

Вывод текста и шрифты mfc

БлогNot. Вывод текста нестандартным графическим шрифтом в Java2ME

Вывод текста нестандартным графическим шрифтом в Java2ME

Разумеется, шрифт придется создать.

Необходимые для вывода текста на графический экран объекты Canvas и Graphics в Java2ME поддерживаются с профиля MIDP1.0, то есть, доступны абсолютно всем телефонам, поддерживающим яву.

А если говорить про вывод текста, то вот рабочий класс, который выводит текст в BufferScreen, а затем этот BufferScreen выводится на экран:

Файл bfnt.png — размером 64х64 пикселя и содержит нарисованные буквы размером 8х8 точек. Для среды WTK достаточно положить такой файл в папку ресурсов (res) проекта.

27.01.2009, 17:21; рейтинг: 11798

Вывод текста (стр. 1 из 3)

Основы вывода текста

Одно из самых больших преимуществ Windows заключается в достаточно легком использовании большого количества разнообразных высококачественных шрифтов. На этой лекции мы должны будем познакомиться с основными правилами вывода текста и применения шрифтов.

Сначала мы будем разбираться с основными средствами вывода текста. С одной из функций, осуществляющих вывод мы уже встречались:

BOOLTextOut( hDC, nXStart, nYStart, lpsString, wLength );

Эта функция осуществляет вывод строки текста, заданной параметром lpsString, длиной wLength символов, начиная с указанной позиции (nXStart, nYStart) на заданном контексте устройства hDC.

Это самая простая функция, осуществляющая вывод текста в Windows. Однако результат ее применения определяется многими дополнительными параметрами.

Во-первых , цвет выведенного текста задается тремя разными атрибутами контекста устройства: текущим цветом текста, текущим цветом фона и режимом заполнения фона. Ранее мы уже рассматривали эти атрибуты и способы их изменения. Для манипуляций с этими атрибутами предназначены функции:

COLORREFSetTextColor( hDC, crColor );

COLORREF GetTextColor( hDC );

COLORREF SetBkColor( hDC, crColor );

COLORREF GetBkColor( hDC );

int SetBkMode( hDC, nMode );

Для задания цвета символов используется функция SetTextColor(), которая устанавливает в качестве цвета символов ближайший чистый цвет, доступный на устройстве.

Для задания цвета фона используются два другие атрибута контекста устройства. Если установлен режим заполнения фона OPAQUE (используется по умолчанию), то для заполнения фона под текстом используется текущий цвет фона, который может и не совпадать с фоном окна, который закрашивается кистью. В тех случаях, когда фон под текстом не надо изменять, используется режим TRANSPARENT (при этом атрибут “цвет фона” игнорируется).

Во-вторых , при выводе текста задается его начальная позиция. Определение и использование этой начальной позиции не так-то и просто. Для того, что бы разобраться с этим, нам придется ввести несколько новых терминов, использующихся для определения размера символов и строк.

Символ может размещаться в своей ячейке на некотором расстоянии от всех четырех границ. Для того, что бы можно было определить точные характеристики символов, предназначена специальная функция:

BOOL GetTextMetrics( hDC, lpTEXTMETRIC );

Эта функция заполняет структуру TEXTMETRIC информацией о текущем применяемом шрифте. Сейчас мы рассмотрим только некоторые поля этой структуры, которые определяют размеры символов. Для этого мы приведем изображение символов, на котором будут проставлены размеры так, как они называются в структуре TEXTMETRIC. Позже мы будем возвращаться к отдельным полям этой структуры.

Обычно за высоту строки принимают сумму размеров tmHeight и tmExternalLeading, что позволяет выводить строки, так, чтобы между ними всегда был небольшой промежуток.

Несколько сложнее получается с определением ширины символа и длины строки. Это связано с тем, что Windows может применять как шрифты с фиксированной шириной символа (fixed pitch ), так и пропорциональные (variable pitch ) шрифты. Поэтому в той информации, которую Вы можете получить о шрифте, используются два параметра: максимальная ширина символа tmMaxCharWidth и средняя ширина символа tmAveCharWidth.

Однако пользоваться обеими этими характеристиками очень не удобно. Применение максимальной ширины может давать сильно завышенный результат, а средняя величина может ошибаться в любую сторону. Практически она дает приличное приближение только для длинных строк английского не специального текста, так как для вычисления средней ширины используются частоты встречаемости английских символов.

На практике используют функции, которые позволяет определить точные размеры строки как в высоту, так и в ширину:

DWORD GetTextExtent( hDC, lpsString, wLength );

BOOL GetTextExtentPoint( hDC, lpsString, wLength, lpSize );

Эти функции, определив размеры строки, возвращают результат либо в виде двойного слова (в младшем слове хранится ширина, в старшем — высота строки), либо в структуре типа SIZE, адрес которой Вы указываете. Вместо структуры SIZE может быть использована структура POINT, (так было раньше, что и дало название функции). Обе эти структуры совпадают.

Однако надо сделать небольшое замечание: когда вычисляется высота строки используются значения высот отдельных символов. Поэтому, если Ваша строка состоит из небольших символов, то ее высота может быть существенно меньше, чем полная высота знакоместа.

В некоторых случаях бывает желательно узнать точную ширину каждого символа. (Например, такое может потребоваться для перемещения каретки или выделения фрагмента текста.) Для этого предназначены функции

BOOLGetCharWidth( hDC, uFirstChar, uLastChar, lpnWidths );

BOOLGetCharABCWidth( hDC, uFirstChar, uLastChar, lpABC );

Функция GetCharWidth() определяет ширину каждого символа, входящего в интервал от uFirstChar до uLastChar и размещает результаты в массиве целых чисел, указанном параметром lpnWidths.

Функция GetCharABCWidth() возвращает более подробную информацию о ширине каждого символа, которая используется только TrueType шрифтами.

Результаты размещаются в массиве структур типа ABC, смысл полей поясним рисунком:

В-третьих , при выводе текста с помощью функции TextOut() (или ExtTextOut()), используется еще один атрибут GDI, который называется “режим выравнивания текста”. Он устанавливается и проверяется с помощью функций:

UINT SetTextAlign( hDC, nAlign );

UINT GetTextAlign( hDC );

Этот атрибут указывает, как надо располагать строку текста относительно указанной начальной точки. Обычно указанная точка задает положение верхнего левого угла строки. Однако с помощью функции SetTextAlign() вы можете установить и иной режим выравнивания.

Параметр nAlign указывает положение какой точки задается:

по горизонтали по вертикали
TA_LEFT (по умолчанию) TA_TOP (по умолчанию)
TA_CENTER TA_BASELINE
TA_RIGHT TA_BOTTOM

В некоторых случаях с помощью режима выравнивания можно обойтись без вычисления ширины строки самим, а переложить эту работу на Windows.

Для этого существует дополнительные режимы выравнивания TA_UPDATECP и TA_NOUPDATECP (по умолчанию). Если используется режим TA_UPDATECP, то координаты, указывающие положение точки вывода текста игнорируются, а вместо них используется атрибут контекста устройства “положение текущей точки”. После операции вывода эта текущая точка перемещается на правую границу выведенного текста.

Для начальной установки текущей точки в нужную позицию (или при переходе со строки на строку) Вы можете воспользоваться функцией:

DWORD MoveTo( hDC, nX, nY );

В-четвертых , на вывод текста влияют атрибуты контекста устройства, выравнивающие длины строк. Для этого GDI содержит две функции, изменяющие интервалы между словами и символами.

intSetTextCharacterExtra( hDC, nExtraSpace );

устанавливает интервал между двумя соседними символами. С ее помощью легко можно “разрядить” строку или слово так, что бы ее длина оказалась равна требуемой величине.

Вторая функция используется для задания ширины символов, используемых для разделения слов в строке. Каждый шрифт содержит символ, который называется “символ–разделитель” (Break Char). Какой символ является разделителем, можно определить по значению поля .tmBreakChar структуры TEXTMETRIC, так как для разных шрифтов могут быть установлены разные символы–разделители. Обычно это пробел.

int SetTextJustification( hDC, nExtraSpace, cBreakChars );

Эта функция изменяет ширину символа–разделителя таким образом, что бы cBreakChars, встреченных в строке, увеличили ее ширину на nExtraSpace единиц.

Дополнительные функции для вывода текста

Рассмотренная функция TextOut() является простейшей. У нее много ограничений. Например, она не распознает управляющих символов в строке типа табуляции, перевода строки и возврата каретки. В некоторых случаях возможно применение иных функций, осуществляющих вывод текста.

Если строка текста содержит символы табуляции, то Вы должны вместо функции TextOut() использовать функцию:

hDC, nXStart, nYStart, lpsString, wLength, cTabStops, lpnTabPositions, nTabOrigin);

Первые 5 параметров этой функции используются так же, как и в функции TextOut(), а три дополнительных применяются таким образом:

Параметр lpnTabPositions содержит массив x–координат точек, в которых происходит остановка табулятора. Он должен быть упорядочен в возрастающем порядке. Параметр cTabStops задает число таких точек.

Есть две особенности в применении этих параметров:

если оба они равны 0, то табулятор будет останавливаться через каждых 8 средних ширин символов.

если массив содержит только одно число N (и cTabStops равно 1), то табулятор будет останавливаться через каждые N единиц.

Параметр nTabOrigin указывает x–координату, начиная от которой отсчитываются положения табулятора. Функция возвращает размер выведенной строки.

С введением этой функции пришлось ввести еще одну функцию, которая определяет размеры строки, содержащей символы табуляции:

DWORD GetTabbedTextExtent( hDC, lpsString, wLength, cTabStops, lpnTabPositions );

Однако эта функция не имеет параметра nTabOrigin, поэтому ее результат может отличаться от результата функции TabbedTextOut(. ).

Существует специальная функция, осуществляющая вывод строки текста и устанавливающая требуемые промежутки между отдельными символами строки:

BOOL ExtTextOut( hDC, nXStart, nYStart, fuOption, lpRect, lpsString, wLength, lpnDx );

Параметры hDC, nXStart, nYStart, lpsString и wLength используютсятакже, какивфункции TextOut(). Два дополнительных параметра fuOption и lpRect задают прямоугольник, в котором осуществляется вывод текста и метод использования этого прямоугольника.

Если fuOption равно ETO_OPAQUE, то указанный прямоугольник закрашивается текущим цветом фона; если fuOption равно ETO_CLIPPED, то прямоугольник ограничивает область вывода текста. Оба параметра fuOption и lpRect могут быть 0, тогда такой прямоугольник не используется.

Основы программирования на языке Си++. Часть IV. Программирование для Microsoft Windows с использованием Visual C++ и библиотеки классов MFC

Для того, чтобы оценить ресурс, необходимо авторизоваться.

Пособие предназначено для обучения студентов, обладающих навыками пользовательской работы на персональном компьютере, основным понятиям и методам современного практического программирования. Предметом изучения курса является объектно-ориентированное программирование на языке Си++ в среде современных 32-х разрядных операционных систем семейства Windows. Программа курса разбита на 4 части: (1) Введение в программирование на языке Си++; (2) Основы программирования трехмерной графики; (3) Объектно-ориентированное программирование на языке Си++ и (4) Программирование для Microsoft Windows с использованием Visual C++ и библиотеки классов MFC. После изучения курса студент получает достаточно полное представление о содержании современного объектно-ориентированного программирования, об устройстве современных операционных систем Win32 и о событийно-управляемом программировании. На практических занятиях вырабатываются навыки программирования на Си++ в интегрированной среде разработки Microsoft Visual C++ 5.0.

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL