Графический интерфейс GDI в Microsoft Windows© Александр Фролов, Григорий ФроловТом 14, М.: Диалог-МИФИ, 1993, 288 стр. 3.4. Использование цветовых палитрВ большинстве случаев вам не потребуется использовать палитры Windows, так как далеко не все приложения работают с большим количеством цветов. Приложения для обычной презентационной графики (схемы, диаграммы и т. п.), обработки текстовых документов и другие аналогичные приложения выглядят вполне удовлетворительно при использовании 16 цветов, обеспечиваемых стандартным драйвером VGA. Более того, увеличение цветового разрешения, как правило, сказывается отрицательно на производительности Windows. И это понятно - чем больше используется цветов, тем больше объем данных, передаваемых из процессора в видеоконтроллер. Несмотря на то что акселераторы Windows значительно ускоряют работу, очень много пользователей имеют дешевые видеоконтроллеры, работающие в режиме низкого или, в крайнем случае, среднего цветового разрешения. Если же ваше приложение должно работать с многокрасочными реалистическими изображениями или выводить на экран битовые изображения, полученные с помощью цветного сканера, задача использования цветовых палитр выдвигается на первый план. Оценивая отношение цветового разрешения к цене для разрешения 256 цветов, многие пользователи склоняются не к акселераторам с объемом видеопамяти 1-2 Мбайт (цена которых находится в диапазоне 100-400 долларов), а к обычным видеоадаптерам SVGA с объемом видеопамяти 512 Кбайт (цена порядка 40 долларов). Возможно, это неправильно, так как акселераторы и в самом деле заметно ускоряют работу Windows, однако едва ли вам понравится идея убеждать потенциальных покупателей вашего приложения обновить свой компьютер. Поэтому приложение должно использовать все цветовые возможности обычных видеоадаптеров SVGA, которые реализуются с помощью механизма цветовых палитр. Вы можете спросить: будет ли ваше приложение, рассчитанное на использование цветовых палитр, работать с современными видеоадаптерами в режиме True Color, для которых механизм палитр не предусмотрен (так как в этом случае он не нужен)? Будет, так как GDI все равно сможет (и даже с большим успехом) удовлетворить цветовые запросы приложения. Механизм реализации логической палитрыВ самом начале этой главы мы рассказали вам о системной цветовой палитре. Вы знаете, что в системной палитре, состоящей из 256 ячеек, 20 ячеек зарезервированы для статических цветов. Остальные 236 ячеек доступны для приложений. Что же приложения могут с ними сделать? Любое приложение может создать свою собственную палитру цветов в виде массива размером до 256 элементов, содержащего данные типа PALETTEENTRY, которые могут хранить RGB-цвета или номера цветов в системной палитре. Подготовив массив, содержащий цвета, приложение может создать логическую палитру , вызвав функцию CreatePalette . Затем палитру нужно выбрать в контекст отображения функцией SelectPalette . Так как на экране могут отображаться только цвета, находящиеся в системной палитре, в процессе реализации палитры приложение должно перенести (или отобразить) цвета из логической палитры в системную палитру, вызвав функцию RealizePalette . Последний шаг называется реализацией палитры . В зависимости от того, как подготовлена логическая палитра, а также от того, является приложение активным или фоновым, механизм реализации может выполняться по-разному. Рассмотрим обычный способ реализации. Первоначально в системной палитре 20 ячеек отмечены как занятые для статических цветов и 236 - как свободные. Когда первое приложение реализует свою логическую палитру, цвета из этой палитры переписываются в свободные ячейки системной палитры, после чего они становятся доступными для отображения. Если свободных ячеек не хватает для размещения всей логической палитры, оставшиеся цвета будут отображены на ближайшие имеющиеся в системной палитре. При этом неизбежны цветовые искажения. Однако в Windows одновременно может работать несколько приложений, одно из которых является активным, а остальные - фоновыми. Для активных и фоновых приложений используется разный механизм реализации логической палитры. Активное приложение имеет больший приоритет в использовании свободных ячеек системной палитры. Как правило, запрос активного приложения (точнее говоря, активного окна) на реализацию логической палитры выполняется полностью, так как перед реализацией все ячейки системной палитры (кроме 20 зарезервированных) отмечаются как свободные. Фоновые приложения довольствуются теми свободными ячейками, что остались после реализации логической палитры активного приложения. Поэтому для фоновых приложений качество "цветопередачи" может быть не очень высоким. На рис. 3.6 показан процесс реализации логической палитры для активного окна.
Рис. 3.6. Реализация логической палитры для активного окна На этом рисунке для наглядности мы уменьшили размер системной палитры. Разные цвета показаны различной штриховкой соответствующих прямоугольников. Из рисунка видно, что после реализации логической палитры активного окна в системной палитре осталось три свободные ячейки. Теперь допустим, что фоновое приложение реализует логическую палитру для своего окна. В его распоряжении есть три свободные ячейки. Процесс реализации логической палитры для фонового окна показан на рис. 3.7.
Рис. 3.7. Реализация логической палитры для фонового окна Часть цветов логической палитры фонового окна уже есть в системной палитре, поэтому они просто отображаются на соответствующие ячейки системной палитры. Три свободные ячейки используются для тех цветов, которых нет в системной палитре. Однако в нашей логической палитре есть цвета, которым нет точного соответствия в системной палитре, причем свободных ячеек тоже не осталось. В этом случае GDI отображает эти цвета на близкие из системной палитры (на рис. 3.7 такое отображение показано пунктирной линией). После такого упрощенного описания процесса реализации логической палитры перейдем к конкретным шагам, необходимым для использования палитр. Проверка возможности использования палитрыДалеко не все устройства отображения способны работать с палитрами. Например, драйвер видеоадаптера VGA палитры не поддерживает, несмотря на то что аппаратура VGA может работать с палитрой. Последнее обстоятельство связано с тем, что размер палитры VGA составляет всего 64 ячейки, что явно недостаточно. Самый лучший способ убедиться в том, что драйвер видеоадаптера способен работать с палитрами, заключается в вызове функции GetDeviceCaps с параметром RASTERCAPS (мы обсуждали эту функцию в 11 томе "Библиотеки системного программиста"). Если в возвращенном слове установлен бит RC_PALETTE , приложение может использовать цветовые палитры. Стандартный размер системной палитры равен 256 ячеек, однако вы можете уточнить это значение, вызвав функцию GetDeviceCaps с параметром SIZEPALETTE . Если драйвер видеоконтроллера не работает с палитрами, размер палитры, определенный с помощью функции GetDeviceCaps, может быть равным 0. Для определения количества системных цветов используйте эту же функцию с параметром NUMRESERVED . С помощью функции GetDeviceCaps можно также определить цветовое разрешение устройства вывода. При этом следует учитывать, что некоторые устройства работают с цветовыми плоскостями. Количество этих плоскостей можно определить, пользуясь значением PLANES . Отметим, что количество цветов, которые могут быть представлены устройством с цветовыми плоскостями, равно 2n, где n - количество цветовых плоскостей. Если устройство работает с цветовыми плоскостями и использует несколько бит для представления цвета одного пиксела, количество одновременно отображаемых цветов можно определить по формуле: nColors = 2(nPixel * nPlanes), где nPixel - количество битов, используемых для представления цвета пиксела (значение BITSPIXEL ); nPlanes - количество цветовых плоскостей (значение PLANES). Значение NUMCOLORS равно количеству статических цветов. Так как палитра может быть изменена (перезагружена), фактически вы можете использовать больше цветов, чем указано в NUMCOLORS. Но в этом случае вы должны сами перезагружать палитру. Для устройств, работающих с палитрами, правильное количество используемых цветов возвращается при использовании значения COLORRES - количество бит цветового разрешения. Для режима с использованием 256 цветов это значение равно 18, что соответствует 6 битам для представления каждого из трех базовых цветов. В 11 томе "Библиотеки системного программиста" мы описали приложение DCAPS, с помощью которого можно исследовать возможности драйверов различных видеоконтроллеров. Приведем параметры, имеющие отношение к цветовому разрешению, полученные с помощью этого приложения для разных режимов.
Из этой таблицы видно, что драйвер VGA не работает с палитрами. В режимах высокого цветового разрешения палитры также не используются. Создание логической палитрыДля того чтобы создать палитру, ваше приложение должно заполнить структуру LOGPALETTE , описывающую палитру, и массив структур PALETTEENTRY , определяющий содержимое палитры. Структура LOGPALETTE и указатели на нее определены в файле windows.h: typedef struct tagLOGPALETTE { WORD palVersion; WORD palNumEntries; PALETTEENTRY palPalEntry[1]; } LOGPALETTE; typedef LOGPALETTE* PLOGPALETTE; typedef LOGPALETTE NEAR* NPLOGPALETTE; typedef LOGPALETTE FAR* LPLOGPALETTE; Поле palVersion для Windows версии 3.0 и 3.1 должно содержать значение 0x300. В поле palNumEntries нужно записать размер палитры (количество элементов в массиве структур PALETTEENTRY). Сразу после структуры LOGPALETTE в памяти должен следовать массив структур PALETTEENTRY, описывающих содержимое палитры: typedef struct tagPALETTEENTRY { BYTE peRed; BYTE peGreen; BYTE peBlue; BYTE peFlags; } PALETTEENTRY; typedef PALETTEENTRY FAR* LPPALETTEENTRY; Поле peFlags определяет тип элемента палитры и может иметь значения NULL, PC_EXPLICIT , PC_NOCOLLAPSE и PC_RESERVED . Если поле peFlags содержит значение NULL, в полях peRed, peGreen и peBlue находятся RGB-компоненты цвета. В процессе реализации логической палитры для этого элемента используется описанный нами ранее алгоритм. Если поле peFlags содержит значение PC_EXPLICIT, младшее слово элемента палитры содержит индекс цвета в системной палитре. Если поле peFlags содержит значение PC_NOCOLLAPSE, в процессе реализации логической палитры данный элемент будет отображаться только на свободную ячейку системной палитры. Если же свободных ячеек нет, используется обычный алгоритм реализации. Последнее возможное значение для поля peFlags (PC_RESERVED) используется для анимации палитры с помощью функции AnimatePalette . Анимация палитры позволяет динамически вносить изменения в палитру. Такой элемент палитры после реализации не подвергается изменениям при реализации других палитр, он становится зарезервированным. После подготовки структуры LOGPALETTE и массива структур PALETTEENTRY приложение может создать логическую палитру, вызвав функцию CreatePalette : HPALETTE WINAPI CreatePalette(const LOGPALETTE FAR* lplgpl); В качестве параметра следует передать функции указатель на заполненную структуру LOGPALETTE. Функция возвращает идентификатор созданной палитры или NULL при ошибке. Выбор палитры в контекст отображенияСозданная палитра перед использованием должна быть выбрана в контекст отображения. Выбор палитры выполняется функцией SelectPalette : HPALETTE WINAPI SelectPalette( HDC hdc, HPALETTE hpal, BOOL fPalBack); Функция выбирает палитру hpal в контекст отображения hdc, возвращая в случае успеха идентификатор палитры, которая была выбрана в контекст отображения раньше. При ошибке возвращается значение NULL. Указав для параметра fPalBack значение TRUE, вы можете заставить GDI в процессе реализации палитры использовать алгоритм, соответствующий фоновому окну. Если же этот параметр равен FALSE, используется алгоритм для активного окна (т. е. все ячейки системной палитры, кроме зарезервированных для статических цветов, отмечаются как свободные и используются для реализации палитры). Реализация палитрыПроцедура реализации палитры заключается в вызове функции RealizePalette : UINT WINAPI RealizePalette(HDC hdc); Возвращаемое значение равно количеству цветов логической палитры, которое удалось отобразить в системную палитру. Рисование с использованием палитрыИтак, вы создали палитру, выбрали ее в контекст отображения и реализовали. Теперь приложение может пользоваться цветами из созданной палитры. Но как? Если приложению нужно создать перо или кисть, определить цвет текста функцией SetTextColor или закрасить область функцией FloofFill (т. е. вызвать одну из функций, которой в качестве параметра передается переменная типа COLORREF, содержащая цвет), вы можете вместо макрокоманды RGB воспользоваться одной из следующих макрокоманд: #define PALETTEINDEX (i) \ ((COLORREF)(0x01000000L | (DWORD)(WORD)(i))) #define PALETTERGB (r,g,b) (0x02000000L | RGB(r,g,b)) Макрокоманда PALETTEINDEX позволяет указать вместо отдельных компонент цвета индекс в логической палитре, соответствующий нужному цвету. Макрокоманда PALETTERGB имеет параметры, аналогичные знакомой вам макрокоманде RGB, однако работает по-другому. Если цвет определен с помощью макрокоманды RGB, в режиме низкого и среднего цветового разрешения для рисования будет использован ближайший к указанному статический цвет. В режиме высокого цветового разрешения полученный цвет будет полностью соответствовать запрошенному. Если же для определения цвета использована макрокоманда PALETTERGB, GDI просмотрит системную палитру и подберет из нее цвет, наилучшим образом соответствующий указанному в параметрах макрокоманды. В любом случае при работе с палитрой GDI не использует для удовлетворения запроса из логической палитры смешанные цвета. Какой из двух макрокоманд лучше пользоваться? На этот вопрос нет однозначного ответа. Макрокоманда PALETTEINDEX работает быстрее, однако с ее помощью можно использовать только те цвета, которые есть в системной палитре. Если ваше приложение будет работать в режиме True Color, лучшего эффекта можно добиться при использовании макрокоманды PALETTERGB, так как для режимов высокого цветового разрешения эта макрокоманда обеспечит более точное цветовое соответствие. Удаление палитрыТак как палитра является объектом, принадлежащим GDI, а не создавшему ее приложению, после использования палитры приложение должно обязательно ее удалить. Для удаления логической палитры лучше всего воспользоваться макрокомандой DeletePalette , определенной в файле windowsx.h: #define DeletePalette(hpal) \ DeleteObject((HGDIOBJ)(HPALETTE)(hpal)) В качестве параметра этой макрокоманде следует передать идентификатор удаляемой палитры. Учтите, что как и любой другой объект GDI, нельзя удалять палитру, выбранную в контекст отображения. Перед удалением следует выбрать старую палитру, вызвав функцию SelectPalette. Сообщения об изменении палитрыЕсли ваше приложение активно работает с палитрами, оно должно иметь в виду, что одновременно вместе с ним могут работать и другие приложения, претендующие на изменение системной палитры. Для того чтобы при передаче фокуса ввода другому приложению изображение, построенное с использованием палитры, не оказалось испорчено в результате изменения системной палитры другим приложением, ваше приложение должно обрабатывать сообщения WM_QUERYNEWPALETTE и WM_PALETTECHANGED. Сообщение WM_QUERYNEWPALETTEСообщение WM_QUERYNEWPALETTE посылается окну, которое становится активным. Это сообщение не имеет параметров. Главное окно приложения может менять свой статус с активного на фоновое несколько раз. Каждый раз, когда оно становится активным, ему посылается сообщение WM_QUERYNEWPALETTE. В ответ на это сообщение приложение должно заново реализовать свою логическую палитру, так как пока его главное окно было неактивно, другое приложение могло изменить системную палитру. Если палитра изменилась, обработчик сообщения WM_QUERYNEWPALETTE должен перерисовать окно. Если обработчик сообщения WM_QUERYNEWPALETTE изменил системную палитру, он должен вернуть ненулевое значение, а если нет - нулевое. Сообщение WM_PALETTECHANGEDКогда любое приложение изменяет системную палитру, все перекрывающиеся (overlapped) и временные (pop up) окна получают сообщение WM_PALETTECHANGED . Это сообщение посылается также в окно приложения, которое выполнило изменение системной палитры. Параметр wParam сообщения WM_PALETTECHANGED содержит идентификатор окна, изменившего системную палитру. Если приложение обрабатывает это сообщение, оно должно вернуть нулевое значение. В ответ на сообщение WM_PALETTECHANGED приложение должно заново реализовать палитру и, если палитра изменилась, перерисовать окно. Вместо полной перерисовки окна можно обновить цвета в окне, вызвав функцию UpdateColors : int WINAPI UpdateColors(HDC hdc); Следует, однако, иметь в виду, что обновление цветов может привести к деградации качества изображения, поэтому при изменении палитры лучше перерисовать окно заново. |