"Графика для Windows средствами DirectDraw" - читать интересную книгу автора (Трухильо Стэн)

Интерфейсы DirectDrawSurface  

Множественные интерфейсы DirectDrawSurface, как и интерфейсы DirectDraw, возникли из-за особенностей спецификации COM. В исходном варианте работа с поверхностями осуществлялась через интерфейс DirectDrawSurface. В DirectX 2 появились новые функциональные возможности, представленные интерфейсом DirectDrawSurface2, а в DirectX 5 возник интерфейс DirectDrawSurface3.

Хотя в этой книге вместо DirectDraw повсюду используется интерфейс DirectDraw2, для работы с поверхностями мы будем придерживаться исходного интерфейса DirectDrawSurface, потому что нововведения интерфейсов DirectDrawSurface2 и DirectDrawSurface3 не слишком важны. В оставшейся части книги термин интерфейс DirectDrawSurface будет обозначать все три интерфейса, если при этом не возникает двусмысленности.

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

• AddAttachedSurface()

• AddOverlayDirtyRect()

• Blt()

• BltBatch()

• BltFast()

• DeleteAttachedSurface()

• EnumAttachedSurfaces()

• EnumOverlayZOrders

• Flip

• GetAttachedSurface()

• GetBltStatus()

• GetCaps()

• GetClipper()

• GetColorKey()

• GetDC()

• GetDDInterface()

• GetFlipStatus()

• GetOverlayPosition()

• GetPalette()

• GetPixelFormat()

• GetSurfaceDesc()

• IsLost()

• Lock()

• PageLock()

• PageUnlock()

• ReleaseDC()

• Restore()

• SetClipper()

• SetColorKey()

• SetOverlayPosition()

• SetPalette()

• SetSurfaceDesc()

• Unlock()

• UpdateOverlay()

• UpdateOverlayDisplay()

• UpdateOverlayZOrder()

Функции описания поверхностей

Мы начнем с четырех функций, с помощью которых можно получить информацию о самой поверхности:

• GetCaps()

• GetPixelFormat()

• GetSurfaceDesc()

• SetSurfaceDesc()

Функция GetCaps() по аналогии с одноименной функцией интерфейса DirectDraw заполняет структуру информацией о том, какие возможности поддерживаются данной поверхностью. В частности, в нее заносятся сведения о том, является ли данная поверхность первичной или внеэкранной, и где она находится — в системной или видеопамяти.

Функция GetPixelFormat() особенно важна при работе с поверхностями форматов High и True Color, поскольку формат пикселей может зависеть от видеокарты. Функция возвращает маски, которые определяют способ хранения отдельных цветовых составляющих.

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

Функция SetSurfaceDesc() (появилась только в DirectX 5 и поддерживается только интерфейсом DirectDrawSurface3) позволяет задать значения некоторых атрибутов поверхности. Например, с ее помощью можно выбрать тип памяти, в которой должна находиться поверхность. Данная функция помогает реализовать нестандартную схему управления поверхностями.

Функции блиттинга

Интерфейс DirectDrawSurface поддерживает три функции, предназначенные для выполнения блиттинга:

• Blt()

• BltBatch()

• BltFast()

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

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

Функция BltFast() является оптимизированным вариантом функции Blt(). Повышение эффективности достигается за счет сокращения возможностей, поэтому BltFast() не умеет выполнять специальные операции блиттинга, поддерживаемые функцией Blt(). Кроме того, BltFast() не выполняет отсечения. Тем не менее функция BltFast() поддерживает блиттинг с использованием цветовых ключей источника и приемника. В сочетании с нестандартными алгоритмами отсечения функция BltFast() обеспечивает выполнение самого быстрого и универсального блиттинга, которого можно добиться от DirectDraw. Нестандартный алгоритм отсечения будет реализован в главе 3.

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

Функция Flip()

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

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

Функции определения состояния поверхностей

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

• GetBltStatus()

• GetFlipStatus()

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

Аналогично, функция GetFlipStatus() показывает, происходит ли в данный момент переключение страниц. Для получения информации о переключении страниц, вызванном функцией Flip(), вместо GetBltStatus() следует пользоваться именно этой функцией даже в том случае, если DirectDraw имитирует переключение страниц посредством блиттинга.

Функции для работы с цветовыми ключами

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

• GetColorKey()

• SetColorKey()

По умолчанию поверхность не имеет цветового ключа. Чаще всего цветовой ключ представляет собой один цвет, однако на некоторых видеокартах возможна работа с интервалами цветовых ключей. Цветовые ключи и их интервалы описываются структурой DDCOLORKEY. Указатели на эту структуру передаются функциям GetColorKey() и SetColorKey() в качестве аргументов. Функции GetColorKey() и SetColorKey() используются при частичном копировании поверхностей, а также при выполнении операций с цветовыми ключами приемника.

Функции Lock() и Unlock()

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

• Unlock()

• Lock()

Функция Lock() возвращает указатель на область памяти, занятую поверхностью, независимо от того, где поверхность находится — в системной памяти или в видеопамяти. Память поверхности всегда организована линейно, что позволяет максимально упростить обращение к графическим данным. Функция Unlock() сообщает DirectDraw о том, что прямой доступ к памяти поверхности завершен.

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

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

Функции GetDC() и ReleaseDC()

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

• GetDC()

• ReleaseDC()

Функция GetDC() предоставляет в ваше распоряжение DC (контекст устройства, Device Context), через который можно осуществлять вывод на поверхность стандартными функциями Win32. Например, передавая его функции Win32 TextOut(), можно вывести на поверхность текст. Функция ReleaseDC() должна быть вызвана сразу же после завершения работы с DC.

Как и в случае с Lock() и Unlock(), функцию ReleaseDC() необходимо вызывать после GetDC() как можно быстрее. Это связано с тем, что внутри функции GetDC() вызывается Lock(), а внутри ReleaseDC() - Unlock().

Функции PageLock() и PageUnlock()

Перейдем к двум функциям, внешне похожим на Lock() и Unlock():

• PageLock()

• PageUnlock()

Вероятно, имена этих функций были выбраны неудачно, потому что они предназначены совсем для других целей. С помощью PageLock() и PageUnlock() можно управлять тем, как Windows обходится с поверхностями в системной памяти. Для работы с ними используется интерфейс DirectDrawSurface2, в DirectDrawSurface они отсутствуют.

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

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

Следует помнить, что частое использование PageLock() приведет к замедлению работы Windows из-за сокращения общего объема переносимой памяти. Когда именно это произойдет, зависит от объема памяти, для которой запрещен перенос на диск, и общего объема системной памяти.

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

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

Функции PageLock() и PageUnlock() не влияют на поверхности, находящиеся в видеопамяти.

Функции IsLost() и Restore()

Две следующие функции предназначены для работы с поверхностями в видеопамяти:

• IsLost()

• Restore()

Рассмотрим следующую ситуацию: ваше приложение начинает работу. Вы размещаете как можно больше поверхностей в видеопамяти, а все остальные — в системной памяти. В течение некоторого времени приложение работает, но затем пользователь запускает другое приложение или переключается на него. Другое приложение может быть чем угодно — скажем, стандартной Windows-программой (например, Windows Explorer или Notepad). Оно также может оказаться другим приложением, которое тоже работает с DirectDraw и стремится разместить как можно больше поверхностей в видеопамяти. Если DirectDraw откажется выделить новому приложению видеопамять, оно будет работать плохо (а то и вообще не будет). Возможна и обратная ситуация — видеопамять окажется недоступной для вашего приложения.

По этой причине DirectDraw может забрать у неактивного приложения видеопамять, занятую некоторыми (или всеми) поверхностями. Такие поверхности называются потерянными (lost). Вообще говоря, такие поверхности остаются у вашей программы, но они перестают быть связанными с какой-либо областью памяти. Любая попытка использовать потерянную поверхность приводит к ошибке DDERR_SURFACELOST. Функция IsLost() позволяет узнать, была ли потеряна память данной поверхности.

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

При этом существует одна загвоздка. Функция Restore() восстанавливает лишь память, закрепленную за поверхностью, но не ее содержимое. Следовательно, после восстановления поверхности ваше приложение само должно восстановить ее содержимое.

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

Функция GetDDInterface()

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

Функции присоединения поверхностей

Интерфейс DirectDrawSurface содержит четыре функции для управления взаимосвязанными поверхностями:

• AddAttachedSurface()

• DeleteAttachedSurface()

• EnumAttachedSurface()

• GetAttachedSurface()

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

Оверлейные функции

Поддержка оверлеев в DirectDrawSurface представлена следующими функциями:

• AddOverlayDirtyRect()

• EnumOverlayZOrder()

• GetOverlayPosition()

• SetOverlayPosition()

• UpdateOverlay()

• UpdateOverlayDisplay()

• UpdateOverlayZOrder()

Функции GetOverlayPosition() и SetOverlayPosition() управляют положением оверлеев. Функция UpdateOverlay() изменяет параметры оверлея; в частности, она определяет, должен ли оверлей отображаться на экране и следует ли применять для него альфа-наложение или копирование с цветовым ключом.

Функция UpdateOverlayDisplay() обновляет изображение с учетом новых значений параметров. Данная функция может обновить все изображение оверлея или ограничиться его прямоугольными областями, заданными функцией AddOverlayDirtyRect(). Наконец, функция EnumOverlayZOrders() используется для перебора оверлеев в порядке их Z-координаты (Z-координата определяет, какие оверлеи выводятся поверх других). Возможен перебор как в прямом порядке (от передних оверлеев к задним), так и в обратном (от задних — к передним).

Функции для работы с объектами отсечения

DirectDraw позволяет присоединить к поверхности экземпляр интерфейса DirectDrawClipper (который мы еще не рассматривали). После того как такое присоединение состоится, операция блиттинга на данную поверхность будет регулироваться объектом отсечения. Для работы с объектами отсечения в интерфейсе DirectDrawSurface имеются две функции:

• GetClipper()

• SetClipper()

Функция SetClipper() присоединяет объект отсечения к поверхности. Функция GetClipper() возвращает указатель на присоединенный ранее объект отсечения. С помощью функции SetClipper() можно разорвать связь между поверхностью и объектом отсечения, для этого в качестве указателя на интерфейс DirectDrawClipper следует задать NULL.

Функции палитры

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

• GetPalette()

• SetPalette()

Функция SetPalette() присоединяет к поверхности экземпляр интерфейса DirectDrawPalette (о нем речь пойдет ниже). Функция GetPalette() применяется для получения указателя на палитру, присоединенную ранее.

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