Графический интерфейс GDI в Microsoft Windows (2)

         

Контекст отображения



1.1. Контекст отображения

Итак, займемся описанием "листа бумаги", на "поверхности" которого выполняется рисование графических изображений и текста - контекста отображения.

Прежде всего уточним понятия контекста отображения и контекста устройства .

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



Инструменты для рисования



1.2. Инструменты для рисования

Итак, теперь у вас есть представление о том, какая "бумага" используется для рисования. Вы смогли убедиться, что ее свойства намного богаче свойств обычной писчей бумаги или даже дорогой бумаги, предназначенной для печати документов или крупных денежных купюр. Теперь посмотрим, какие "карандаши" может использовать приложение Windows для рисования. Можно предположить, что при такой бумаге и карандаши тоже будут не простые...

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

Расскажем кратко об основных особенностях и областях применения перечисленных выше инструментов.



Основные понятия



1. Основные понятия



1.1. Контекст отображения

1.2. Инструменты для рисования

Интерфейс графических устройств GDI операционной системы Microsoft Windows (в дальнейшем - просто GDI), как это можно предположить из названия, предназначен для взаимодействия приложений Windows с графическими устройствами, такими как видеомонитор, принтер или плоттер.

Когда приложения обращаются к GDI для выполнения операции вывода графического изображения, они работают не с реальными (физическими) устройствами вывода, а с логическими. Приложения Windows не определяют тип видеомонитора (EGA, VGA или SVGA), а работают с логическим видеомонитором, имеющим феноменальные характеристики: способность отображать практически любой цвет, огромное разрешение и т. д. Выполняя запрос приложения, GDI обращается к драйверу соответствующего устройства вывода. Драйвер работает непосредственно с физическим устройством вывода. В процессе выполнения запроса GDI (или драйвер) учитывает ограниченные возможности физического устройства вывода и его аппаратные особенности, делая необходимые приближения.

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

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

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

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

Интерфейс GDI - одна из наиболее сложных компонент Microsoft Windows версии 3.1. В Windows NT она получила дальнейшее развитие, однако все базовые понятия сохранились.

Из чего состоит интерфейс GDI с точки зрения приложения?

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

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

Функции рисования не имеют параметров, указывающих цвет или толщину линии. Такие параметры хранятся в контексте отображения.

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

Изучение интерфейса GDI мы начнем с подробного описания контекста отображения и всех его атрибутов (мы уже немного рассказывали вам о контексте отображения в 11 томе "Библиотеки системного программиста"). Затем мы займемся инструментами для рисования и расскажем о некоторых функциях программного интерфейса GDI, предназначенных для рисования простейших геометрических объектов, таких как точки, линии, эллипсы, многоугольники и т. п.



Получение и освобождение контекста отображения



2.1. Получение и освобождение контекста отображения

Способы получения (и, соответственно, освобождения) контекста отображения разные для контекстов разного типа. Можно выделить следующие типы контекста отображения: общий контекст отображения (common display context); контекст отображения для класса окна (class display context); личный контекст отображения (private display context); родительский контекст отображения (parent display context); контекст отображения для окна (window display context); контекст физического устройства (device context); информационный контекст (information context); контекст для памяти (memory device context); контекст для метафайла (metafile context).

Каждый из перечисленных выше контекстов имеет свои особенности и свое назначение.



Выбор режима отображения



2.2. Выбор режима отображения

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

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

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



Рисование геометрических фигур



2.3. Рисование геометрических фигур

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

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

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

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

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

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



Приложение LINER



2.4. Приложение LINER

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



Области



2.5. Области

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



Приложение REGIONS



2.6. Приложение REGIONS

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

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



Сохранение и восстановление контекста отображения



2.7. Сохранение и восстановление контекста отображения

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

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

Для сохранения атрибутов контекста отображения следует использовать функцию SaveDC : int WINAPI SaveDC(HDC hdc);

Значение, возвращаемое этой функцией, необходимо использовать в качестве параметра nSavedDC для функции RestoreDC , восстанавливающей атрибуты контекста отображения: BOOL WINAPI RestoreDC(HDC hdc, int nSavedDC);

Функция RestoreDC возвращает значение TRUE при успешном завершении или FALSE при ошибке.

В качестве значения параметра nSavedDC можно использовать -1. В этом случае будет восстановлен контекст, сохраненный при последнем вызове функции SaveDC.



Приложение DASHLINE



2.8. Приложение DASHLINE

Приложение DASHLINE демонстрирует использование функции LineDDA для рисования пунктирных линий увеличенной толщины (Рисунок 2.28). Напомним, что вы не можете создать перо для рисования таких линий обычными средствами.



Работа с контекстом отображения



2. Работа с контекстом отображения

2.1. Получение и освобождение контекста отображения

2.2. Выбор режима отображения

2.3. Рисование геометрических фигур

2.4. Приложение LINER

2.5. Области

2.6. Приложение REGIONS

2.7. Сохранение и восстановление контекста отображения

2.8. Приложение DASHLINE

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

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

Последнее действие (освобождение или удаление контекста отображения) должно быть обязательно выполнено. Самый простой способ полностью нарушить работоспособность Windows - забыть освободить полученный контекст отображения или удалить созданный контекст отображения или устройства.

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

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

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



Битовые изображения в формате DDB



4.1. Битовые изображения в формате DDB

Как мы уже говорили, битовые изображения в формате DDB являются аппаратно-зависимыми. Поэтому структура изображения в оперативной памяти зависит от особенностей аппаратуры.

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

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

Изображения DIB, в отличие от изображений DDB, являются аппаратно-независимыми, поэтому без дополнительного преобразования их нельзя отображать на экране с помощью функций BitBlt и StretchBlt. В операционной системе Windows версий 3.х битовые изображения хранятся в файлах с расширением имени bmp, при этом используется аппаратно-независимый формат DIB.



Приложение BMPLOGO



4.2. Приложение BMPLOGO

Приведем пример приложения BMPLOGO, которое демонстрирует работу с битовыми изображениями в формате DDB. Это приложение создает одно битовое изображение в памяти и рисует его в верхнем левом углу окна, а также (что на наш взгляд, самое интересное), рисует текстовую строку с оттенением (Рисунок 4.3).



Битовые изображения в формате DIB



4.3. Битовые изображения в формате DIB


Как мы уже говорили, битовые изображения DDB имеют один существенный недостаток - они "привязаны" к конкретному типу устройства вывода. В графической оболочке Presentation Manager операционной системы OS/2 впервые был использован аппаратно-независимый формат для хранения изображений, который называется DIB.

В операционной системе Windows версии 3.0 этот формат получил свое дальнейшее развитие. В частности, была добавлена возможность хранения изображения в компрессованном виде. К сожалению, использованный алгоритм компрессии дает хорошие результаты только для таких изображений, которые содержат большие одинаково закрашенные области. Операционная система Windows NT позволяет использовать новые форматы изображений DIB и произвольные методы компрессии, такие как, например, JPEG (очень эффективный метод компрессии графических изображений, при котором можно ценой потери качества получить практически любую степень сжатия).

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



Рисование изображений DIB



4.4. Рисование изображений DIB

Процесс рисования изображений DIB включает в себя несколько этапов.

Сначала необходимо загрузить bmp-файл в оперативную память и убедиться в том, что этот файл действительно содержит изображение DIB. Ваше приложение может полностью проигнорировать bmp-файлы в формате Presentation Manager (как это делает, например, приложение Paintbrush в Windows версии 3.1) или выполнить их преобразование в формат Windows, что намного лучше. Следует также проверить формат заголовка BITMAPINFOHEADER.

Затем нужно определить размер таблицы цветов (если она есть). Если в DIB есть таблица цветов, ее следует преобразовать в палитру. Непосредственно перед рисованием изображения DIB созданная палитра должна быть выбрана в контекст отображения и реализована. Если bmp-файл содержит изображение с высоким цветовым разрешением, в файле нет таблицы цветов. В этом случае нет необходимости создавать палитру.

После создания палитры следует определить адрес битов изображения. Напомним, что смещение битов изображения находится в поле bfOffBits структуры BITMAPFILEHEADER. Если содержимое этого поля равно нулю, можно вычислить адрес битов изображения исходя из размера заголовков и размера таблицы цветов.

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

Первый способ заключается в преобразовании изображения DIB в изображение DDB с помощью функции SetDIBits. Полученное таким образом изображение DDB может быть выбрано в контекст памяти и нарисовано обычным способом при помощи функции BitBlt или StretchBlt.

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

Если изображение DIB содержит таблицу цветов и устройство вывода способно работать с цветовыми палитрами, ваше приложение должно обрабатывать сообщения WM_PALETTECHANGED и WM_QUERYNEWPALETTE. Для обработки этих сообщений можно использовать алгоритм, описанный в главе "Цвет и цветовые палитры".



Приложение BMPINFO



4.5. Приложение BMPINFO

После всего сказанного выше у вас могло сложиться впечатление, что процедура рисования изображений DIB значительно труднее, чем процедура рисования файлов DDB. В самом деле, для отображения содержимого bmp-файла вы должны считать его в память, проверить формат всех структур, при необходимости создать палитру и следить за ее изменениями со стороны других приложений. В момент рисования вам нужно подготовить значительное количество структур, указателей и параметров. Увы, для рисования изображений DIB в программном интерфейсе GDI операционной системы Windows версии 3.1 нет ничего более удобного, чем описанные нами функции.

Большинство приложений, тем не менее, нуждается в рисовании изображений DIB. Для иллюстрации методов работы с такими изображениями мы подготовили приложение BMPINFO. Это приложение умеет загружать bmp-файлы любых "легальных" форматов Windows и Presentation Manager версии 1.x, и выводит соответствующую информацию из заголовков этих файлов, однако оно способно рисовать только некомпрессованные изображения в формате Windows. Как мы уже говорили, в большинстве случаев это как раз именно то, что нужно.

В меню "File" есть строки "Open", "Info..." и, конечно, "Exit". С помощью строки "Open" вы можете выбрать bmp-файл. Если это некомпрессованный файл в формате Windows, он рисуется в окне приложения (Рисунок 4.7).



Битовые изображения



4. Битовые изображения

4.1. Битовые изображения в формате DDB

4.2. Приложение BMPLOGO

4.3. Битовые изображения в формате DIB

4.4. Рисование изображений DIB

4.5. Приложение BMPINFO

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

В операционной системе Windows используются два формата битовых изображений - аппаратно-зависимый DDB (device-dependent bitmap ) и аппаратно-независимый DIB (device-independent bitmap ).

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

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

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

Аппаратно-независимое битовое изображение DIB содержит описание цвета пикселов изображения, которое не зависит от особенностей устройства отображения. Операционная система Windows после соответствующего преобразования может отобразить такое изображение на любом устройстве вывода. Несмотря на некоторое замедление процесса вывода по сравнению с выводом изображений DDB, универсальность изображений DIB делает их весьма привлекательными для хранения изображений.

Вместе с Windows версии 3.0 и 3.1 поставляется несколько файлов, имеющих расширение имени bmp. Эти файлы содержат битовые изображения в формате DIB. Версии 2.х операционной системы Windows могли работать только с аппаратно-зависимыми изображениями DDB, что создавало определенные трудности при необходимости отображения последних на различных устройствах вывода.

Создавая приложение, которое должно работать с bmp-файлами, следует учитывать, что существует несколько форматов таких файлов. Файлы битовых изображений могут содержать таблицу цветов или цветовую палитру, могут быть сжаты с использованием алгоритмов RLE4 или RLE8 . Кроме того, коммерческая версия приложения должна уметь работать с битовыми изображениями в формате оболочки Presentation Manager операционной системы OS/2. Было бы также неплохо, если бы приложение могло анализировать структуру bmp-файла с целью обнаружения возможных ошибок, так как отображение содержимого неправильного файла может создать полный хаос на экране.

Кроме битовых изображений используются так называемые векторные изображения. Если битовые изображения содержат описание цвета каждого пиксела, векторные изображения состоят из описаний отдельных графических объектов, из которых состоит изображение. Это могут быть линии, окружности и т.п. Некоторые графические редакторы, например, Corel Draw, Microsoft Draw, Micrografx Designer, для внешнего представления изображения используют векторный формат.

Сравнивая эти форматы, отметим, что каждый из них имеет свои преимущества и свои недостатки.

Битовые изображения, как правило, выводятся на экран быстрее, так как их внутренняя структура аналогична (до некоторой степени) структуре видеопамяти. Изображения, получаемые при помощи сканеров и цифровых видеокамер (таких, как PhotoMan фирмы Logitec) получаются именно как битовые изображения.

К недостаткам битовых изображений можно отнести большой объем памяти, требующийся для их хранения (около 1 Мбайт в режиме True Color), невозможность масштабирования без потери качества изображения, а также сложность выделения и изменения отдельных объектов изображения.

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

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

Существует множество форматов файлов, предназначенных для хранения битовых и векторных изображений. В этой главе мы будем подробно рассматривать только формат файлов с расширением имени bmp (мы будем называть их bmp-файлами). Эти файлы хранят битовые изображения и используются в различных версиях операционной системы Microsoft Windows, Microsoft Windows NT и в графической оболочке Presentation Manager операционной системы OS/2. Векторные изображения можно хранить в виде метафайлов.



Классификация шрифтов



5.1. Классификация шрифтов

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

Можно сказать, что шрифт состоит из изображений (рисунков) отдельных символов - глифов (glyph).

Для внутреннего представления глифа в файле шрифта True Type используются описания контуров, причем один глиф может содержать несколько контуров (Рисунок 5.1).



Выбор шрифта в контекст отображения



5.2. Выбор шрифта в контекст отображения

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

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

Обратим ваше внимание на одно важное обстоятельство.

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



Приложение FONTVIEW



5.3. Приложение FONTVIEW

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

Меню "Font" приложения FONTVIEW позволяет вам выбрать шрифт двумя способами - вы можете указать семейство шрифта или выбрать конкретный шрифт при помощи диалоговой панели "Font".

Меню "Orientation" позволяет задать угол поворота текстовой строки, выводя ее с наклоном (Рисунок 5.3) или даже перевернутой "вверх ногами" (Рисунок 5.4).



Получение информации о шрифте



5.4. Получение информации о шрифте

В программном интерфейсе GDI имеется несколько функций, с помощью которых приложение может получить различную информацию о шрифте, выбранном в контекст отображения. Наибольший интерес представляют метрики шрифта, о которых мы рассказывали в 11 томе "Библиотеки системного программиста", однако для масштабируемых шрифтов True Type можно получить и другую информацию.



Шрифты



5. Шрифты

5.1. Классификация шрифтов

5.2. Выбор шрифта в контекст отображения

5.3. Приложение FONTVIEW

5.4. Получение информации о шрифте

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

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

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

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

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

Масштабируемые шрифты True Type , доступные приложениям Windows, не только сохраняют свое начертание при произвольном изменении высоты букв, но и обладают другими достоинствами.

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

Еще одно преимущество масштабируемых шрифтов True Type связано с тем, что вы можете встроить такой шрифт непосредственно в документ. Зачем это может понадобиться?

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

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

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



Контекст отображения для принтера



6.1. Контекст отображения для принтера

На первый взгляд, контекст отображения для принтера получить нетрудно - достаточно вызвать функцию CreateDC , указав имя драйвера, имя устройства и имя порта вывода, к которому подключен принтер: HDC WINAPI CreateDC( LPCSTR lpszDriver, // имя драйвера LPCSTR lpszDevice, // имя устройства LPCSTR lpszOutput, // имя файла или порта вывода const void FAR* lpvInitData); // данные для инициализации

Созданный при помощи функции CreateDC контекст устройства следует удалить после использования, вызвав функцию DeleteDC : BOOL WINAPI DeleteDC(HDC hdc);

Как мы уже говорили, параметр lpszDriver является указателем на строку символов, содержащую имя драйвера, обслуживающего физическое устройство. Имя драйвера совпадает с именем файла *.drv, содержащего драйвер. Этот драйвер находится в системном каталоге Windows.

Имя устройства lpszDevice - это название устройства.

Параметр lpszOutput указывает на структуру данных типа DEVMODE, используемую при инициализации устройства вывода. Если при работе с устройством нужно использовать параметры, установленные при помощи приложения Control Panel, параметр lpszOutput следует указать как NULL.

Однако как определить эти параметры?

Текущий принтер описан в файле win.ini в разделе [windows]: [windows] ... ... device=HP LaserJet III,hppcl5a,LPT1: ...

Вы можете получить контекст отображения для текущего принтера, указав эти параметры функции CreateDC: hdc = CreateDC("hppcl5a", "HP LaserJet III", "LPT1:", NULL);

Однако к компьютеру может быть подключено несколько принтеров. Например, к порту LPT1: может быть подключен лазерный принтер, а к порту LPT2: - матричный или струйный принтер.

Список подключенных принтеров, имена драйверов и портов ввода/вывода можно найти в разделе [devices] файла win.ini: [devices] Epson FX-850=EPSON9,LPT1: HP LaserJet III=hppcl5a,LPT1:

Ваше приложение может получить параметры принтера, используемого по умолчанию, а также параметры всех установленных принтеров непосредственно из файла win.ini. Это можно легко сделать при помощи функции GetProfileString: int GetProfileString( LPCSTR lpszSection; // адрес раздела LPCSTR lpszEntry; // адрес элемента раздела LPCSTR lpszDefault; // адрес строки по умолчанию LPSTR lpszReturnBuffer; // адрес буфера для записи int cbReturnBuffer; // размер буфера

Параметр lpszSection должен указывать на имя раздела, в нашем случае на строку "windows". Через параметр lpszEntry передается адрес текстовой строки, содержащий имя элемента раздела, в нашем случае это адрес строки "device".

Если указанного элемента или раздела нет в файле win.ini, используется строка по умолчанию, адрес которой передается через параметр lpszDefault.

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

Для получения контекста отображения текущего принтера обычно используется такая функция: HDC PASCAL GetPrinterDC() { char msgbuf[128]; LPSTR pch; LPSTR pchFile; LPSTR pchPort; // Определяем текущий принтер из файла win.ini if(!GetProfileString("windows", "device", "", msgbuf, sizeof(msgbuf))) return NULL; // Выполняем разбор строки для выделения имени драйвера, // имени устройства и имени порта ввода/вывода for(pch=msgbuf; *pch && *pch != ','; pch=AnsiNext(pch)); if(*pch) *pch++ = 0; // Пропускаем управляющие символы и символ табуляции while(*pch && *pch <= ' ') pch=AnsiNext(pch); pchFile = pch; while(*pch && *pch != ',' && *pch > ' ') pch = AnsiNext(pch); if(*pch) *pch++ = 0; while(*pch && (*pch <= ' ' || *pch == ',')) pch = AnsiNext(pch); pchPort = pch; while(*pch && *pch > ' ') pch = AnsiNext(pch); *pch = 0; // Возвращаем контекст отображения для принтера return CreateDC(pchFile, msgbuf, pchPort, NULL); }

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

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

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

Для получения списка установленных принтеров вы можете воспользоваться все той же функцией GetProfileString , указав в качестве первого параметра адрес строки "devices", а в качестве второго - значение NULL: GetProfileString("devices", NULL, "", msgbuf, sizeof(msgbuf));

В этом случае в буфер будут переписаны все строки из раздела [devices], каждая строка будет закрыта двоичным нулем, последняя строка будет закрыта двумя двоичными нулями.

Однако есть еще одна задача, которую должно уметь решать ваше приложение.

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

Заметим, что эти функции экспортируются драйвером как обычной DLL-библиотекой (драйвер принтера и есть DLL-библиотека). Для вызова одной из этих функций вы должны загрузить драйвер явным образом, вызвав функцию LoadLibrary , а затем получить адрес точки входа при помощи функции GetProcAddress . DLL-библиотеки и перечисленные выше функции мы описали в 13 томе "Библиотеки системного программиста".

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

К счастью, DLL-библиотека commdlg.dll содержит функцию PrintDlg, способную решить все перечисленные выше задачи и вдобавок получить для выбранного принтера контекст отображения, который можно использовать для рисования на принтере (или печати, если вам так больше нравится).



Функция PrintDlg



6.2. Функция PrintDlg

С помощью функции PrintDlg приложение может вывести на экран одну из двух диалоговых панелей, представленных на Рисунок 6.1 и 6.2, с помощью которых пользователь может напечатать документ, выбрать нужный принтер или изменить его параметры.



Функции для печати



6.3. Функции для печати

В операционной системе Windows версии 3.0 и более ранних версий для печати использовалась одна функция Escape, которая имела множество подфункций (63 подфункции). Начиная с версии 3.1 вместо этой функции рекомендуется использовать несколько других.

Чаще всего используются семь функций.

StartDoc

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

StartPage

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

EndPage

Функция EndPage завершает процесс печати страницы. Метафайл, созданный в процессе печати одной страницы, проигрывается на принтере.

EndDoc

Функция завершает процесс печати документа. Она вызывается один раз после завершения печати документа.

AbortDoc

Эта функция предназначена для аварийного завершения процесса печати документа.

SetAbortProc

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

ResetDC

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

Как пользоваться этими функциями?

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

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

Приведем прототип функции SetAbortProc : int SetAbortProc(HDC hdc, ABORTPROC abrtprc);

Первый параметр определяет контекст устройства, для которого устанавливается функция отмены печати, второй - адрес функции отмены печати, полученный при помощи функции MakeProcInstance .

Функция отмены печати может выглядеть, например, следующим образом: BOOL CALLBACK _export AbortFunc(HDC hdc, int nCode) { MSG msg; while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { if(!hdlgAbort || !IsDialogMessage(hdlgAbort, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return(!fAbort); }

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

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

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

В качестве функции диалога, предназначенного для отмены печати, можно использовать, например, такую функцию: BOOL CALLBACK _export AbortDlgFunc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_INITDIALOG: { fAbort = FALSE; hdlgAbort = hdlg; return TRUE; } case WM_COMMAND: { if (wParam == IDOK || wParam == IDCANCEL) { fAbort = TRUE; return TRUE; } return FALSE; } case WM_DESTROY: { hdlgAbort = 0; return FALSE; } } return FALSE; }

Основной задачей данной функции является установка глобального флага отмены печати fAbort: fAbort = TRUE;

Итак, мы получили контекст принтера, установили для этого контекста процедуру отмены печати, вывели на экран диалоговую панель с кнопкой "Cancel", позволяющую отменить печать. Теперь можно приступать к печати документа.

Прежде всего необходимо вызвать функцию StartDoc : int StartDoc( HDC hdc, // контекст принтера DOCINFO FAR* lpdi); // адрес структуры DOCINFO

Перед вызовом этой функции нужно подготовить структуру DOCINFO , адрес которой передается через параметр lpdi: typedef struct { int cbSize; LPCSTR lpszDocName; LPCSTR lpszOutput; } DOCINFO;

Поле cbSize должно содержать размер структуры в байтах.

Поле lpszDocName должно содержать указатель на текстовую строку, закрытую двоичным нулем, в которой записано название документа.

Через поле lpszOutput можно передать адрес строки, содержащей имя файла (если печать должна быть переназначена в файл). Поле может содержать NULL, в этом случае выполняется печать на принтере.

Далее можно начинать цикл печати страниц документа.

Печать страницы документа начинается с вызова функции StartPage : int StartPage(HDC hdc);

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

После вызова функции StartPage приложение может начинать печать страницы, вызывая обычные функции GDI, такие как TextOut, BitBlt, StretchBlt, Rectangle и т. п.

Завершив печать одной страницы, приложение должно вызвать функцию EndPage : int EndPage(HDC hdc);

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

При успешном завершении функция возвращает положительное значение, отличное от нуля, при ошибке - нуль или значение, меньшее нуля

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

После успешной печати всех страниц документа нужно вызвать функцию EndDoc : int EndDoc(HDC hdc);

Если печать была отменена, или произошла ошибка, вместо этой функции следует вызвать функцию AbortDoc : int AbortDoc(HDC hdc);

Если в процессе печати нужно изменить параметры принтера для отдельных страниц документа, вы можете вызвать до функции StartPage функцию ResetDC : HDC ResetDC( HDC hdc, // контекст печати const DEVMODE FAR* lpdm); // адрес структуры DEVMODE

Эта функция изменяет контекст печати на основе информации, представленной в структуре DEVMODE.

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

Учтите, что функция StartPage запрещает изменение контекста печати, поэтому вы не сможете изменить параметры печати в процессе печати страницы.



Приложение PRNFILE



6.4. Приложение PRNFILE

Для иллюстрации всего сказанного выше мы немного изменили приложение TEDIT, описанное в 12 томе "Библиотеки системного программиста", добавив в него возможность печати. В главном окне этого простейшего редактора текста появилась кнопка "Print", с помощью которой вы можете распечатать текст на любом установленном в системе принтере (Рисунок 6.5).



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



6. Работа с принтером

6.1. Контекст отображения для принтера

6.2. Функция PrintDlg

6.3. Функции для печати

6.4. Приложение PRNFILE

Приложения Windows работают с принтером совсем не так, как программы MS-DOS. Последние используют для печати BIOS (прерывание INT17h ) или функцию 05h прерывания MS-DOS INT 21h . Некоторые программы даже используют порты ввода/вывода параллельного или последовательного интерфейса, к которому подключен принтер. Мы описали все эти средства в первой части второго тома "Библиотеки системного программиста" (стр. 156).

Заметим, что создание программы MS-DOS, способной работать с любым принтером в символьном и графическом режимах, - далеко не простая задача. В мире созданы десятки различных моделей принтеров, каждая из которых имеет свою систему команд и другие особенности. Практически приложение должно иметь собственный набор драйверов для обеспечения возможности работы с любыми моделями принтеров. Текстовый процессор Microsoft Word for DOS версий 4.0 - 6.0 может работать со многими типами принтеров благодаря богатому набору принтерных драйверов, поставляющихся вместе с ним. Тем не менее этого набора иногда оказывается недостаточно.

Ситуация с принтерами в MS-DOS напоминает ситуацию с видеоконтроллерами - так как MS-DOS не имеет в своем составе драйверы видеоконтроллеров, каждая программа MS-DOS должна иметь собственные драйверы.

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

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

Специальное приложение Print Manager позволяет организовать очередь печати. Разные приложения могут помещать в эту очередь свои данные (задания на печать), которые будут выводиться на принтер в фоновом режиме в порядке поступления в очередь (пользователь может изменять расположение заданий на печать в очереди). Это приложение описано в документации пользователя операционной системы Windows. Вы также можете найти его описание во втором томе нашей серии книг "Персональный компьютер. Шаг за шагом", посвященный Windows.

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

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

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



Атрибуты контекста отображения



Атрибуты контекста отображения

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

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



Биты изображения



Биты изображения

Формат области битов изображения одинаковый для некомпрессованых bmp-файлов Windows и bmp-файлов Presentation Manager (оболочка Presentation Manager операционной системы OS/2 версии 1.х не работает с компрессованными файлами).

Каждая строка битового изображения (scan line) хранится в буфере, длина которого кратна двойному слову DWORD. Для черно/белых изображений каждый бит буфера используется для представления цвета одного пиксела (если не используется компрессия).

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

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

В памяти изображение хранится в перевернутом виде. Поэтому, например, изображение, показанное на Рисунок 4.1 после вывода будет иметь вид, показанный на Рисунок 4.6.



Цвет фона



Цвет фона

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

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



Цвет текста



Цвет текста

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

Для выбора цвета текста приложение должно использовать функцию SetTextColor .



Цветовая палитра



Цветовая палитра

Цветовая палитра (color palette ) - это таблица цветов. Некоторые устройства отображения способны работать с десятками тысяч цветов, однако из-за ограничений аппаратуры в них можно использовать одновременно только некоторые из них. Типичный пример - видеоконтроллеры SVGA в режиме отображения 256 цветов. Приложения Windows могут составить для себя палитру из 236 цветов (20 цветов используются системой) и использовать ее для вывода таких изображений, как, например, цветные фотографии, преобразованные в двоичные данные при помощи сканера.

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

Для работы с палитрами в составе программного интерфейса определены такие функции, как CreatePalette , SelectPalette , RealizePalette , UnrealizeObject .



DIBCreatePalette



DIBCreatePalette

Функция создает логическую палитру на базе таблицы цветов изображения DIB, возвращая идентификатор созданной палитры или NULL при ошибке. Если DIB не содержит таблицу цветов, палитра не создается.

Прототипы всех описанных выше функций, а также необходимые константы и типы данных описаны в файле dib.hpp (листинг 4.9).

Листинг 4.9. Файл bmpinfo/dib.hpp #define WINRGB_DIB 1 #define WINRLE4_DIB 2 #define WINRLE8_DIB 3 #define PM_DIB 10 typedef HGLOBAL HDIB; typedef unsigned char huge* LPDIB; HFILE DIBSelectFile(void); HDIB DIBReadFile(HFILE hfDIBFile, DWORD *dwFileSize); BOOL DIBInfo(HDIB hDib, DWORD dwFileSize); int DIBType(HDIB hDib); WORD DIBNumColors(LPDIB lpDib); WORD DIBHeight(LPDIB lpDib); WORD DIBWidth(LPDIB lpDib); HPALETTE DIBCreatePalette(HDIB hDIB); BOOL DIBPaint(HDC hDC, int x, int y, HDIB hDIB);

Обратите внимание на тип LPDIB, который описан как указатель типа huge: typedef unsigned char huge* LPDIB;

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

Файл определения ресурсов приложения BMPINFO приведен в листинге 4.10.

Листинг 4.10. Файл bmpinfo/bmpinfo.rc #include "bmpinfo.hpp" APP_MENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "&Open", CM_FILEOPEN MENUITEM "&Info...", CM_FILEINFO MENUITEM SEPARATOR MENUITEM "E&xit", CM_FILEEXIT END POPUP "&Help" BEGIN MENUITEM "&About...", CM_HELPABOUT END END

Файл определения модуля приложения BMPINFO вы сможете найти в листинге 4.11.

Листинг 4.11. Файл bmpinfo/bmpinfo.def ; ============================= ; Файл определения модуля ; ============================= NAME BMPINFO DESCRIPTION 'Приложение BMPINFO, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 8120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple



DIBFindBits



DIBFindBits

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



DIBHeight



DIBHeight

Функция возвращает высоту изображения DIB в пикселах как значение поля biHeight структуры BITMAPINFOHEADER.



DIBInfo



DIBInfo

Функция выводит информацию о загруженном изображении DIB. Идентификатор блока памяти, содержащего файл изображения, передается этой функции в качестве первого параметра. Через второй параметр передается размер файла, определенный функцией чтения файла DIBReadFile.



DIBNumColors



DIBNumColors

Эта функция определяет размер таблицы цветов. Если изображение DIB содержит таблицу цветов уменьшенного размера, возвращается размер таблицы из поля biClrUsed структуры BITMAPINFOHEADER. В противном случае размер таблицы цветов определяется исходя из содержимого поля biBitCount структуры BITMAPINFOHEADER (количество бит памяти, определяющих цвет пиксела).



DIBPaint



DIBPaint

Эта функция рисует изображение DIB. Первый параметр определяет контекст отображения. Второй и третий - координаты верхнего левого угла прямоугольной области, в которой будет нарисовано изображение. Через последний параметр передается идентификатор блока памяти, содержащего загруженное изображение DIB.

Рисование выполняется при помощи функции StretchDIBits.



DIBPaintBlt



DIBPaintBlt

Функция аналогична предыдущей, однако перед рисованием выполняется преобразование DIB в DDB. Далее преобразованное изображение выводится через контекст памяти в контекст отображения при помощи функции BitBlt.



DIBReadFile



DIBReadFile

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

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

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

Так как размер bmp-файлов обычно больше 64 Кбайт, для чтения используется функция _hread.



DIBSelectFile



DIBSelectFile

Эта функция выводит на экран стандартную диалоговую панель "Open", с помощью которой можно выбрать bmp-файл.

Функция возвращает идентификатор открытого файла или NULL, если пользователь отказался от выбора.



DIBType



DIBType

Функция получает в качестве параметра идентификатор блока памяти, содержащего загруженный файл изображения DIB. Она определяет его формат и выполняет проверку полей структур заголовка.

Если в качестве параметра этой функции было передано значение NULL, она возвращает код ошибки-2. Если не удалось зафиксировать блок памяти, возвращается код ошибки -1.

Функция проверяет первые два байта bmp-файла. Если они не содержат значения 0x4d42, возвращается код ошибки 0.

Далее функция определяет формат заголовка. Для изображения в стандарте Windows проверяются поля biPlanes, biBitCount и biCompression структуры BITMAPINFOHEADER, а для изображения в стандарте Presentation Manager - поля biPlanes и biBitCount структуры BITMAPCOREHEADER.



DIBWidth



DIBWidth

Функция возвращает ширину изображения DIB в пикселах как значение поля biWidth структуры BITMAPINFOHEADER.



Другие функции для работы с изображениями DDB



Другие функции для работы с изображениями DDB

Если вы создали изображение DDB, то пользуясь его идентификатором, нетрудно скопировать в него новый массив бит, соответствующий новому изображению. Для этой цели можно воспользоваться функцией SetBitmapBits : LONG WINAPI SetBitmapBits( HBITMAP hbmp, DWORD cBits, const void FAR* lpvBits);

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

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

Обратную операцию (чтение массива памяти изображения) можно выполнить при помощи функции GetBitmapBits : LONG WINAPI GetBitmapBits( HBITMAP hbmp, LONG cbBuffer, void FAR* lpvBits);

Эта функция копирует байты изображения hbmp в массив lpvBits, причем размер массива указывается через параметр cbBuffer.

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

Если вам нужно создать цветное битовое изображение DDB, можно воспользоваться функцией CreateCompatibleBitmap : HBITMAP WINAPI CreateCompatibleBitmap( HDC hdc, // контекст отображения int nWidth, // ширина изображения int nHeight); // высота изображения

Эта функция создает неинициализированное изображение шириной nWidth пикселов и высотой nHeight пикселов, причем формат изображения соответствует контексту отображения hdc.

Ваше приложение может выбрать неинициализированное изображение, созданное при помощи функции CreateCompatibleBitmap, в контекст памяти (совместимый с тем же контекстом отображения). Затем оно может нарисовать в контексте памяти все что угодно, используя обычные функции GDI, такие как LineTo (передавая им в качестве первого параметра идентификатор контекста памяти). После этого приложение может вызвать функцию BitBlt для отображения результата в окне приложения.

Если желательно создать удаляемое (discardable) битовое изображение, вместо предыдущей функции вы можете вызвать функцию CreateDiscardableBitmap : HBITMAP WINAPI CreateDiscardableBitmap( HDC hdc, // контекст отображения int nWidth, // ширина изображения int nHeight); // высота изображения

Она имеет параметры, аналогичные параметрам функции CreateCompatibleBitmap. Если изображение, созданное с использованием этой функции, не выбрано в контекст памяти, оно может быть удалено. При попытке выбрать в контекст удаленное изображение функция SelectBitmap вернет нулевое значение. В этом случае необходимо удалить изображение макрокомандой DeleteBitmap, после чего создать его заново.



Другие операции



Другие операции

Для изменения прямоугольной области можно воспользоваться функцией SetRectRgn , изменяющей координаты существующей прямоугольной области: void WINAPI SetRectRgn( HRGN hrgn, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);

Изменение области выполняется быстрее ее удаления с последующим повторный созданием новой.

Для перемещения области предназначена функция OffsetRgn : int WINAPI OffsetRgn(HRGN hrgn, int nX, int nY);

Эта функция сдвигает область на nX логических единиц по горизонтали и на nY логических единиц по вертикали, возвращая такие же значения, что и функция CombineRegion.

Вы можете сравнить две области при помощи функции EqualRgn : BOOL WINAPI EqualRgn(HRGN hrgn1, HRGN hrgn2);

Эта функция возвращает TRUE, если области совпадают, и FALSE - если нет.

С помощью функции GetRgnBox можно узнать координаты воображаемого прямоугольника, в который вписана область: int WINAPI GetRgnBox(HRGN hrgn, RECT FAR* lprc);

Эта функция возвращает такие же значения, что и функция CombineRegion

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

Функция PtInRegion возвращает TRUE, если точка (nX,nY) находится внутри области hrgn: BOOL WINAPI PtInRegion(HRGN hrgn, int nX, int nY);

Аналогично для прямоугольной области: BOOL WINAPI RectInRegion(HRGN hrgn, const RECT FAR* lprc);



Физическая система координат



Физическая система координат

На Рисунок 2.1 показана физическая система координат для экрана видеомонитора.



Flags



Flags

Это поле должно содержать флаги инициализации:

PD_ALLPAGES

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

PD_SELECTION

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

PD_PAGENUMS

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

PD_NOSELECTION

Переключатель "Selection" должен находиться в заблокированном состоянии.

PD_NOPAGENUMS

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

PD_COLLATE

Переключатель "Collate" должен находиться во включенном состоянии.

PD_PRINTTOFILE

Переключатель "Print to File" должен находиться во включенном состоянии.

PD_PRINTSETUP

При вызове функции PrintDlg вместо диалоговой панели "Print" отображается диалоговая панель "Print Setup".

PD_NOWARNING

Отмена вывода сообщения о том, что в системе не установлен принтер по умолчанию.

PD_RETURNDC

Функция PrintDlg должна вернуть в поле hDC идентификатор контекста устройства, который можно использовать для печати.

PD_RETURNIC

Функция PrintDlg должна вернуть в поле hDC идентификатор информационного контекста, который можно использовать для получения информации о принтере.

PD_RETURNDEFAULT

После возвращения из функции PrintDlg поля hDevMode и hDevNames будут содержать идентификаторы блоков памяти структур DEVMODE и DEVNAMES, заполненных параметрами принтера, выбранного по умолчанию. Если указан флаг PD_RETURNDEFAULT, перед вызовом функции PrintDlg поля hDevMode и hDevNames должны содержать значения NULL, в противном случае функция вернет признак ошибки.

PD_SHOWHELP

В диалоговой панели необходимо отобразить кнопку "Help".

PD_ENABLEPRINTHOOK

Разрешается использовать функцию фильтра для диалоговой панели "Print".

PD_ENABLESETUPHOOK

Разрешается использовать функцию фильтра для диалоговой панели "Print Setup".

PD_ENABLEPRINTTEMPLATE

Разрешается использовать шаблон диалоговой панели "Print", определяемой полями hInstance и lpPrintTemplateName.

PD_ENABLESETUPTEMPLATE

Разрешается использовать шаблон диалоговой панели "Print Setup", определяемой полями hInstance и lpSetupTemplateName.

PD_ENABLEPRINTTEMPLATEHANDLE

Поле hPrintTemplate содержит идентификатор блока памяти с загруженным шаблоном диалоговой панели "Print". Содержимое поля hInstance игнорируется.

PD_ENABLESETUPTEMPLATEHANDLE

Поле hSetupTemplate содержит идентификатор блока памяти с загруженным шаблоном диалоговой панели "Print Setup". Содержимое поля hInstance игнорируется.

PD_USEDEVMODECOPIES

Орган управления "Copies" блокируется, если принтерный драйвер не способен печатать несколько копий.

PD_DISABLEPRINTTOFILE

Блокируется переключатель "Print to File".

PD_HIDEPRINTTOFILE

Переключатель "Print to File" блокируется и удаляется из диалоговой панели.



Формат bmpфайлов Presentation Manager



Формат bmp-файлов Presentation Manager

Оболочка Presentation Manager операционной системы OS/2 использует другой формат bmp-файла (Рисунок 4.5).



Формат bmpфайлов Windows



Формат bmp-файлов Windows

Формат bmp-файлов для операционной системы Windows версий 3.0 и 3.1 представлен на Рисунок 4.4 (в более старых версиях bmp-файлы содержали битовые изображения в формате DDB).



Форматы файлов и структур данных



Форматы файлов и структур данных

Если рассматривать структуру bmp-файлов в общем, для нас интересны два формата. Первый формат используется в Windows версий 3.х, второй - в графической оболочке Presentation Manager операционной системы OS/2 версий 1.х. Первый из этих форматов является естественным для приложений Windows. Что же касается второго формата, такие приложения, как Paint Brush, способны конвертировать его в стандартный формат Windows.

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



Функция ChooseFont



Функция ChooseFont

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

DLL-библиотека commdlg.dll содержит функцию ChooseFont , специально предназначенную для выбора одного из зарегистрированных в системе шрифтов. Эта функция выводит на экран диалоговую панель "Font", с помощью которой пользователь может выбрать шрифт, стиль шрифта, размер шрифта, цвет букв, может выбрать подчеркнутый или перечеркнутый шрифт (Рисунок 5.2).



Функция EnumFontFamilies



Функция EnumFontFamilies

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

Приведем прототип функции EnumFontFamilies: int EnumFontFamilies( HDC hdc, // идентификатор контекста отображения LPCSTR lpszFamily, // адрес имени семейства шрифта FONTENUMPROC fntenmprc, // адрес функции обратного вызова LPARAM lParam); // произвольные данные

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

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

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

Приложение может передать функции обратного вызова произвольные 32-разрядные данные через параметр lParam.

Функция обратного вызова должна быть определена следующим образом (для функции можно использовать любое имя): int CALLBACK _export EnumFontFamProc( NEWLOGFONT FAR* lpnlf, // адрес структуры NEWLOGFONT NEWTEXTMETRIC FAR* lpntm, // адрес структуры метрики // физического шрифта int FontType, // тип шрифта LPARAM lParam); // адрес произвольных данных, переданных // функции EnumFontFamilies через // параметр lParam

Когда функция EnumFontFamProc получает управление, через параметр lpnlf передается адрес структуры NEWLOGFONT , которая по непонятной причине не описана в файле windows.h. Эта структура аналогична структуре LOGFONT, но имеет в конце два дополнительных поля, определенных только для шрифтов True Type: typedef struct tagNEWLOGFONT { int lfHeight; int lfWidth; int lfEscapement; int lfOrientation; int lfWeight; BYTE lfItalic; BYTE lfUnderline; BYTE lfStrikeOut; BYTE lfCharSet; BYTE lfOutPrecision; BYTE lfClipPrecision; BYTE lfQuality; BYTE lfPitchAndFamily; char lfFaceName[LF_FACESIZE];

BYTE lfFullName[2 * LF_FACESIZE];// только для True Type BYTE lfStyle[LF_FACESIZE]; // только для True Type } NEWLOGFONT;

Поле lfFullName представляет собой массив, содержащий полное имя шрифта, которое состоит из названия шрифта и названия стиля.

В поле lfStyle находится название стиля шрифта.

Параметр lpntm функции EnumFontFamProc содержит адрес структуры NEWTEXTMETRIC , описывающей метрику шрифта: typedef struct tagNEWTEXTMETRIC { int tmHeight; int tmAscent; int tmDescent; int tmInternalLeading; int tmExternalLeading; int tmAveCharWidth; int tmMaxCharWidth; int tmWeight; BYTE tmItalic; BYTE tmUnderlined; BYTE tmStruckOut; BYTE tmFirstChar; BYTE tmLastChar; BYTE tmDefaultChar; BYTE tmBreakChar; BYTE tmPitchAndFamily; BYTE tmCharSet; int tmOverhang; int tmDigitizedAspectX; int tmDigitizedAspectY; // Дополнительные поля DWORD ntmFlags; UINT ntmSizeEM; UINT ntmCellHeight; UINT ntmAvgWidth; } NEWTEXTMETRIC; typedef NEWTEXTMETRIC* PNEWTEXTMETRIC; typedef NEWTEXTMETRIC NEAR* NPNEWTEXTMETRIC; typedef NEWTEXTMETRIC FAR* LPNEWTEXTMETRIC;

Структура NEWTEXTMETRIC аналогична структуре TEXTMETRIC, за исключением четырех дополнительных полей, добавленных в конце. Эти поля описывают физические атрибуты шрифта True Type.

Поле ntmFlags может содержать значения NTM_REGULAR, NTM_BOLD или NTM_ITALIC, соответственно, для нормального, жирного или наклонного шрифта True Type.

Поле ntmSizeEM содержит ширину буквы "М", в единицах, использованных при разработке шрифта.

В поле ntmCellHeight находится высота шрифта в единицах, использованных при разработке шрифта.

Поле ntmAvgWidth содержит ширину шрифта в единицах, использованных при разработке шрифта.

Параметр FontType функции EnumFontFamProc определяет тип шрифта и может содержать одно из следующих значений:



Функция GetDCEx



Функция GetDCEx

В программном интерфейсе операционной системы Windows версии 3.1 появилась функция GetDCEx , предоставляющая расширенные возможности для получения контекста отображения: HDC WINAPI GetDCEx(register HWND hwnd, HRGN hrgnClip, DWORD flags);

Функция возвращает идентификатор полученного контекста отображения или NULL при ошибке.

Параметр hwnd задает идентификатор окна, для которого необходимо получить контекст отображения.

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

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



HDC



hDC

Контекст устройства или информационный контекст.

Это поле заполняется после возвращения из функции PrintDlg, если в поле Flags указано одно из значений: PD_RETURNDC или PD_RETURNIC. В первом случае возвращается контекст принтера, который можно использовать для печати, во втором - информационный контекст, который можно использовать для получения разнообразной информации о принтере.



HDevMode



hDevMode

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

Если содержимое этого поля указать как NULL, после возвращения из функции PrintDlg поле будет содержать идентификатор глобального блока памяти, заказанного функцией. В этом блоке памяти будет расположена структура DEVMODE, заполненная выбранными параметрами принтера. Структура DEVMODE будет описана позже.