• Что такое DirectDraw?
  • Спецификация COM фирмы Microsoft
  • DirectDraw API
  • Интерфейсы DirectDraw и DirectDraw2
  • Интерфейсы DirectDrawSurface  
  • Интерфейс DirectDrawPalette  
  • Интерфейс DirectDrawClipper  
  • Дополнительные интерфейсы DirectDraw  
  • Структуры DirectDraw 
  • Создание приложений DirectDraw  
  • Подготовка инструментов 
  • Глава 1. Краткий курс DirectDraw

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

    И все же я решил попробовать.

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

    Кроме того, я пропускаю многословные рассуждения о HAL (Hardware Abstraction Layer, прослойка абстрактной аппаратуры), HEL (Hardware Emulation Layer, прослойка эмуляции аппаратуры) и все кошмарные диаграммы, которые встречаются в справочных файлах SDK и некоторых книгах по DirectDraw. Вы читаете эту книгу, чтобы освоить программирование для DirectDraw, а не потому, что собираетесь писать драйверы устройств DirectDraw или изучать тонкости внутреннего устройства библиотеки.

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

    Что такое DirectDraw?

    Весьма интересное определение DirectDraw можно найти у одного из его самых яростных противников — FastGraph. Графический пакет FastGraph появился уже довольно давно. В настоящее время существует версия FastGraph, которая поддерживает DirectDraw, но скрывает DirectDraw API за своим собственным нестандартным API. Тед и Диана Грубер (Ted and Diana Gruber), создатели и поставщики FastGraph, разместили на своем Web-узле файл, в котором доказывается, что FastGraph лучше DirectDraw.

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

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

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

    Термины и концепции

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

    Видеорежимы

    Видеорежимом называется набор параметров, поддерживаемый аппаратурой видеокарты (видеоадаптера) и позволяющий организовать вывод графического изображения. Самым известным атрибутом видеорежима является разрешение экрана. По умолчанию в Windows используется видеорежим с разрешением 640×480. Это означает, что на экране выводится 640 пикселей по горизонтали и 480 пикселей по вертикали. Также часто встречаются видеорежимы с разрешением 800×600 и 1024×768. Некоторые видеокарты поддерживают так называемые режимы ModeX. Типичный режим ModeX имеет разрешение 320×200.

    Видеорежимы также различаются по глубине пикселей (pixel depth). Этот параметр определяет количество различных значений, принимаемых отдельным пикселем, и, следовательно, количество отображаемых цветов. Например, в видеорежиме с глубиной пикселей в 8 бит каждый пиксель может иметь один из 256 различных цветов. В режимах с 16-битной глубиной пикселей поддерживается отображение до 65536 цветов. Глубина пикселей обычно равна 8, 16, 24 или 32 битам.

    Видеорежимы реализуются специальным устройством, установленным на компьютере, — видеокартой. На видеокарте устанавливается отдельная память, не входящая в основную память компьютера. Память, установленную на видеокарте, мы будем называть видеопамятью, а обычную память (RAM) — системной памятью. Объем памяти, необходимой для поддержки определенного видеорежима, определяется разрешением и глубиной пикселей в этом режиме. Например, для видеорежима 640×480×8 (640×480 пикселей, каждый из которых имеет глубину в 8 бит) требуется 307300 байт. Видеорежим 1024×768×16 требует 1572864 байт. Для поддержки видеорежимов используется видеопамять. Следовательно, режимы, поддерживаемые конкретной видеокартой, ограничиваются объемом установленной видеопамяти. Скажем, режим 1024×768×16 не поддерживается видеокартами с 1 Мбайт памяти, потому что для него такой объем памяти недостаточен.

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

    Аппаратное ускорение

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

    Поверхности

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

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

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

    Первичные поверхности часто бывают составными (complex), или, что то же самое, переключаемыми (flippable). Переключаемая поверхность может участвовать в переключении страниц — операции, при которой содержимое всей поверхности мгновенно отображается на экране с помощью специальных аппаратных средств. Переключение страниц используется во многих графических программах как с поддержкой DirectDraw, так и без, поскольку оно обеспечивает очень гладкую анимацию и устраняет мерцание. Переключаемая первичная поверхность на самом деле состоит из двух поверхностей, одна из которых отображается на экране, а другая — нет. Невидимая поверхность называется вторичным буфером (back buffer). При переключении страниц поверхности меняются местами: та, которая была вторичным буфером, отображается на экране, а та, что ранее отображалась, превращается во вторичный буфер.

    Как внеэкранные, так и первичные поверхности делятся на две разновидности: палитровые (palettized) и беспалитровые (non-palettized). Палитровая поверхность вместо конкретных значений цветов содержит индексы в цветовой таблице, которая называется палитрой. В DirectDraw палитровыми являются только 8-битные поверхности. Поверхности с глубиной пикселей, равной 16, 24 и 32 битам, являются беспалитровыми. Вместо индексов в них хранятся фактические значения цветов.

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

    Блиттинг

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

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

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

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

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

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

    Палитры

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

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

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

    Отсечение

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

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

    DirectDraw обеспечивает полноценную поддержку прямоугольного отсечения. Тем не менее в некоторых ситуациях бывает полезно написать свою собственную процедуру отсечения. Нестандартное отсечение рассматривается в главе 3.

    Другие типы поверхностей

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

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

    Альфа-поверхности (alpha channel surface) используются для выполнения альфа-наложения (alpha blending). Альфа-наложение является более сложной формой прозрачности и позволяет осуществлять «полупрозрачное» копирование поверхностей. Альфа-поверхность может использоваться для управления прозрачностью отдельных пикселей. Такие поверхности имеют глубину в 1, 2, 4 или 8 бит. Альфа-поверхность с глубиной 1 бит поддерживает лишь два уровня прозрачности: нулевой (непрозрачный пиксель) и стопроцентный (полностью прозрачный пиксель). С другой стороны, 8-битные альфа-поверхности позволяют задавать до 256 различных степеней прозрачности. Альфа-наложение относится к числу возможностей, не эмулируемых в DirectDraw. Следовательно, для использования альфа-наложения необходимо иметь видеокарту, обладающую соответствующими аппаратными средствами, или же написать собственную функцию для программной эмуляции альфа-наложения.

    Z-буферы и поверхности 3D-устройств используются в трехмерных приложениях. Эти типы поверхностей были включены в DirectDraw специально для поддержки Direct3D. Z-буферы используются при визуализации трехмерных сцен; они определяют, какие объекты сцены находятся ближе к зрителю и, следовательно, отображаются перед другими объектами. Поверхности 3D-устройств могут использоваться для синтеза трехмерных изображений в DirectDraw. Z-буферы и поверхности 3D-устройств в этой книге не рассматриваются.

    Спецификация COM фирмы Microsoft

    Библиотека DirectDraw реализована в соответствии со спецификацией COM (многокомпонентная модель объекта, Component Object Model) фирмы Microsoft. Спецификация COM предназначена для создания стопроцентно переносимых программных компонентов, наделенных возможностью безопасного обновления. О COM можно рассказать довольно много, но эта книга посвящена другой теме. Мы рассмотрим COM лишь в объеме, необходимом для использования DirectDraw.

    В COM используется объектно-ориентированная модель, более жесткая, чем модели, принятые в языках типа C++. Так, доступ к COM-объектам всегда осуществляется с помощью функций. COM-объекты не могут иметь открытых переменных. Кроме того, наследование в COM выглядит ограниченным по сравнению с C++.

    Объекты и интерфейсы

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

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

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

    Интерфейс IUnknown содержит три функции, наследуемые всеми COM-интерфейсами.

    • AddRef()

    • Release()

    • QueryInterface

    Функции AddRef() и Release() обеспечивают поддержку такого средства COM, как инкапсуляция времени существования (lifetime encapsulation). Она представляет собой протокол, согласно которому каждый объект сам отвечает за свое уничтожение.

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

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

    Функция QueryInterface() позволяет обратиться к COM-объекту с запросом о том, поддерживает ли он тот или иной интерфейс. Вспомните, например, что обновленные COM-объекты предоставляют дополнительные интерфейсы, не изменяя существующих. Если данный интерфейс не поддерживается запрашиваемым объектом, возвращается указатель на альтернативный интерфейс.

    GUID

    Чтобы обратиться к объекту с запросом о поддержке некоторого интерфейса, используя функцию QueryInterface(), необходимо как-то идентифицировать этот интерфейс. Для этого используется значение GUID (глобально-уникального идентификатора, Globally Unique IDentifier) данного интерфейса. GUID представляет собой 128-битное значение, уникальное для всех практических целей. Значения GUID всех интерфейсов DirectDraw включены в заголовочные файлы DirectX.

    Такого краткого введения в COM вполне достаточно для эффективной работы с DirectDraw API. Далее, по мере обсуждения DirectDraw API, вы поймете, насколько важна эта информация.

    DirectDraw API

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

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

    Библиотека DirectDraw оформлена в виде четырех COM-объектов. Доступ к каждому объекту осуществляется через один или несколько интерфейсов. Вот их полный список:

    • DirectDraw

    • DirectDraw2

    • DirectDrawSurface

    • DirectDrawSurface2

    • DirectDrawSurface3

    • DirectDrawPalette

    • DirectDrawClipper

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

    Интерфейсы DirectDraw и DirectDraw2

    В первоначальном варианте библиотеки DirectX (еще в те времена, когда она называлась Game SDK) вся основная функциональность DirectDraw была сосредоточена в интерфейсе DirectDraw. Позднее, с выходом DirectX 2, рабочий интерфейс был усовершенствован. В соответствии со спецификацией COM интерфейс DirectDraw не изменился, а для работы с новыми возможностями использовался новый интерфейс DirectDraw2. Следует заметить, что интерфейс DirectDraw2 представляет собой расширение DirectDraw. Он предоставляет все возможности интерфейса DirectDraw, а также ряд дополнительных. При работе с DirectX версий 2 и выше можно выбирать между интерфейсом DirectDraw и DirectDraw2. Поскольку DirectDraw2 делает все то же, что и DirectDraw, а также многое другое, вряд ли можно найти какие-то доводы в пользу работы с DirectDraw. Кроме того, Microsoft выступает против хаотичного, непоследовательного использования этих интерфейсов. По этим причинам во всех программах, приведенных в книге, будет использован интерфейс DirectDraw2.

    Ниже перечислены все функции интерфейсов DirectDraw и DirectDraw2 (в алфавитном порядке):

    • Compact()

    • CreateClipper()

    • CreatePalette()

    • CreateSurface()

    • DuplicateSurface()

    • EnumDisplayModes()

    • EnumSurfaces()

    • FlipToGDISurface()

    • GetAvailableVidMem()

    • GetCaps()

    • GetDisplayMode()

    • GetFourCCCodes()

    • GetGDISurface()

    • GetMonitorFrequency()

    • GetScanLine()

    • GetVerticalBlankStatus()

    • RestoreDisplayMode()

    • SetCooperativeLevel()

    • SetDisplayMode()

    • WaitForVerticalBlank()

    Далее рассмотрены функции интерфейса DirectDraw. Обратите внимание на то, что в оставшейся части этой главы термин интерфейс DirectDraw относится как к интерфейсу DirectDraw, так и к DirectDraw2. Уточнения будут приведены лишь в тех случаях, когда функция отличается в двух интерфейсах.

    Функции создания интерфейсов

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

    • CreateClipper()

    • CreatePalette()

    • CreateSurface()

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

    Функция CreatePalette() создает экземпляры интерфейса DirectDrawPalette. Палитры, как и интерфейс DirectDrawClipper, используются не всеми приложениями DirectDraw. Например, приложению, работающему только с 16-битными видеорежимами, палитра не понадобится. Тем не менее приложение, работающее в 8-битном видеорежиме, должно создать хотя бы один экземпляр DirectDrawPalette.

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

    Экземпляры самого интерфейса DirectDraw создаются функцией DirectDraw Create(). DirectDrawCreate() - одна из немногих самостоятельных функций DirectDraw, не принадлежащих никакому COM-интерфейсу.

    Функция GetCaps()

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

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

    СОВЕТ

    DirectX Viewer

    В DirectX SDK входит программа DXVIEW, которая сообщает о возможностях всех компонентов DirectX, в том числе и DirectDraw. На большинстве компьютеров информация о DirectDraw отображается в двух категориях: Primary Display Driver и Hardware Emulation Layer. Первая категория сообщает о возможностях аппаратных видеосредств. Во второй перечислены возможности, эмулируемые DirectDraw при отсутствии аппаратной поддержки. На компьютерах с двумя и более видеокартами, поддерживаемыми DirectDraw, DXVIEW выводит сведения о способностях каждой из них.

    Функция SetCooperativeLevel()

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

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

    Интерфейс DirectDraw содержит четыре функции для работы с видеорежимами:

    • EnumDisplayModes()

    • GetDisplayMode()

    • RestoreDisplayMode()

    • SetDisplayMode()

    С помощью функции EnumDisplayModes() можно получить от DirectDraw список доступных видеорежимов. По умолчанию EnumDisplayModes() перечисляет все видеорежимы, но по описаниям можно исключить из списка режимы, не представляющие для вас интереса. Функция EnumDisplayModes() не обязана присутствовать в программе, однако это желательно, если вы собираетесь организовать переключение видеорежимов. На рынке существует огромное количество видеоустройств, каждое из которых обладает своими возможностями и ограничениями. Не стоит полагаться на автоматическую поддержку любого конкретного видеорежима, за исключением принятого по умолчанию в Windows режима 640×480×8.

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

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

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

    Помимо функции CreateSurface() интерфейс DirectDraw содержит следующие функции для работы с поверхностями:

    • DuplicateSurface()

    • EnumSurfaces()

    • FlipToGDISurface()

    • GetGDISurface()

    • GetAvailableVidMem()

    • Compact()

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

    Функция EnumSurfaces() используется для перебора всех поверхностей, удовлетворяющих заданному критерию. Если критерий не указан, составляется список всех существующих поверхностей.

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

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

    Функция GetAvailableVidMem() возвращает объем текущей доступной видеопамяти. Эта функция присутствует в интерфейсе DirectDraw2, но отсутствует в DirectDraw. С ее помощью приложение может определить, сколько поверхностей ваше приложение сможет создать в видеопамяти.

    Функция Compact() не реализована в DirectX, однако в будущем она обеспечит механизм дефрагментации видеопамяти. Если ваше приложение постоянно создает и уничтожает поверхности, находящиеся в видеопамяти, дефрагментация может высвободить немало места.

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

    Интерфейс DirectDraw содержит четыре функции, относящихся не к видеокарте, а к устройству отображения (монитору):

    • GetMonitorFrequency()

    • GetScanLine()

    • GetVerticalBlankStatus()

    • WaitForVerticalBlank()

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

    Функция GetMonitorFrequency() возвращает текущую частоту смены кадров монитора. Эта частота обычно измеряется в герцах (Гц). Например, частота в 60 Гц означает, что состояние экрана обновляется 60 раз в секунду.

    Функция GetScanLine() возвращает номер строки развертки (горизонтального ряда пикселей), обновляемой в данный момент. Она не поддерживается некоторыми комбинациями видеокарт и мониторов. Если данная способность не поддерживается, функция возвращает код ошибки DDERR_UNSUPPORTED.

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

    Функция GetFourCCCodes()

    Наш обзор интерфейса DirectDraw завершается функцией GetFourCCCodes(). Она возвращает коды FourCC, поддерживаемые видеокартой. Коды FourCC используются для описания YUV-поверхностей, не относящихся к стандарту RGB. Мы не будем рассматривать такие поверхности, так как они выходят за рамки этой книги.

    Интерфейсы 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() применяется для получения указателя на палитру, присоединенную ранее.

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

    Интерфейс DirectDrawPalette  

    Интерфейс DirectDrawPalette предназначен для работы с палитровыми видеорежимами и поверхностями. Несмотря на то что в Windows поддерживается ряд видеорежимов с глубиной пикселей менее 8 бит, DirectDraw поддерживает лишь 8-битные палитровые режимы.

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

    Интерфейс DirectDrawPalette содержит всего три функции:

    • GetCaps()

    • GetEntries()

    • SetEntries()

    Функция GetCaps() определяет возможности палитры. В числе получаемых сведений — количество элементов палитры, поддержка палитрой вертикальной синхронизации и (в случае 8-битной палитры) возможность заполнения всех 256 элементов.

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

    Экземпляры интерфейса DirectDrawPalette присоединяются к поверхности функцией SetPalette() интерфейса DirectDrawSurface. Палитровая анимация выполняется либо присоединением разных палитр к первичной поверхности, либо изменением содержимого палитры функцией SetEntries()

    Интерфейс DirectDrawClipper  

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

    Экземпляры интерфейса DirectDrawClipper создаются функцией CreateClipper() интерфейса DirectDraw. Интерфейс DirectDrawClipper содержит следующие функции:

    • SetHWnd()

    • GetHWnd()

    • IsClipListChanged()

    • SetClipList()

    • GetClipList()

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

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

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

    После того как экземпляр DirectDrawClipper будет присоединен к поверхности, происходит автоматическое отсечение операций блиттинга, выполняемых функциями Blt(), BltBatch() и UpdateOverlay(). Обратите внимание на то, что в список не входит функция BltFast(). Для нее отсечение не поддерживается. 

    Дополнительные интерфейсы DirectDraw  

    Строго говоря, DirectDraw содержит еще три интерфейса, не рассмотренных нами:

    • DDVideoPortContainer

    • DirectDrawColorControl

    • DirectDrawVideoPort

    Эти интерфейсы, появившиеся в DirectX 5, предназначены для низкоуровневого управления видеопортами. Точнее, они предоставляют средства для потоковой пересылки «живого видео» на поверхности DirectDraw. Хотя с их помощью можно организовать в приложениях DirectDraw поддержку работы с видео, это не рекомендуется, за исключением случаев, когда высокоуровневые видео-API не отвечают вашим потребностям. В книге эти интерфейсы не рассматриваются.

    Структуры DirectDraw 

    После рассмотрения всех интерфейсов и функций DirectDraw мы переходим к структурам данных. Всего в DirectDraw определено восемь структур:

    • DDBLTFX

    • DDCAPS

    • DDOVERLAYFX

    • DDPIXELFORMAT

    • DDSURFACEDESC

    • DDSCAPS

    • DDBLTBATCH

    • DDCOLORKEY

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

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

    Например, фрагмент для работы со структурой DDSURFACEDESC может выглядеть так:

    DDSURFACEDESC surfdesc;

    surfdesc.dwSize = sizeof(surfdesc);

    surf->GetSurfaceDesc(&surfdesc);

    Сначала мы объявляем структуру, затем присваиваем полю dwSize значение, используя функцию sizeof(). После этого структура передается функции GetSurfaceDesc() интерфейса DirectDrawSurface. Если забыть присвоить значение полю dwSize, вызов функции закончится неудачей.

    На первый взгляд это выглядит глупо. С какой радости DirectDraw настаивает на передаче размера структуры, в ней же и определенной? Причина, по которой эти пять структур содержат поле dwSize, состоит в том, что в будущем они могут измениться. DirectDraw будет проверять размер структуры и по нему определять ее версию. Сейчас DirectDraw требует передачи правильного размера, чтобы приучить к этому разработчиков. Позднее это окупится, поскольку дальнейшие версии DirectDraw смогут корректно работать со старыми программами DirectDraw.

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

    DDSURFACEDESC surfdesc;

    ZeroMemory(&surfdesc, sizeof(surfdesc));

    surfdesc.dwSize = sizeof(surfdesc);

    surf->GetSurfaceDesc(&surfdesc);

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

    Создание приложений DirectDraw  

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

    Оконные приложения 

    Все приложения DirectDraw делятся на два основных типа: оконные (windowed) и полноэкранные (full-screen). Оконное приложение DirectDraw выглядит, как обычная Windows-программа. О полноэкранных приложениях речь пойдет ниже.

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

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

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

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

    Когда нехватка памяти заставляет вас создавать внеэкранные буфера в системной памяти, быстродействие заметно падает. К сожалению, эта ситуация встречается очень часто, особенно для видеокарт с памятью объемом 2 Мбайт, потому что для рабочего стола Windows большинство пользователей выбирает режимы с высоким разрешением. Например, в режиме 1024×768×16 только для первичной поверхности требуется почти 2 Мбайт памяти. При наличии только 2 Мбайт видеопамяти для внеэкранных поверхностей остается слишком мало места.

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

    Наконец, в оконных приложениях не удается полностью управлять содержимым палитры. Так как Windows резервирует 20 элементов палитры, можно задать лишь 236 из 256 возможных цветов. Зарезервированные цвета занимают первые и последние 10 элементов системной палитры. Следовательно, на долю палитровых изображений остаются лишь 236 «средних» цветов. 

    Полноэкранные приложения 

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

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

    Так как полноэкранные приложения не ограничиваются текущим видеорежимом Windows, проблема с наличием свободной видеопамяти становится менее серьезной. При наличии всего 2 Мбайт видеопамяти можно сэкономить память за счет установки видеорежима с низким разрешением. Если будет обнаружено 4 Мбайт видеопамяти, ваше приложение сможет выбрать более требовательный видеорежим и при этом обеспечить хорошее быстродействие.

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

    Комбинированные приложения 

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

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

    Подготовка инструментов 

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

    • Windows NT или Windows 95

    • Runtime-файлы DirectX

    • DirectX SDK

    • Visual C++ 5.0

    Скорее всего, операционная система Windows NT или Windows 95 уже установлена на вашем компьютере. Если вы пользуетесь Windows NT, понадобится версия 4.0 или выше (желательно с установленным Service Pack 3); для Windows 95 подойдет любая версия. Три оставшиеся строки из списка мы рассмотрим немного подробнее. 

    Runtime-файлы DirectX 

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

    Runtime-часть DirectX встроена в Windows NT версии 4.0 и выше. В Windows 95 этих файлов нет, но их можно легко найти и установить. К сожалению, в Windows NT версий ниже 4.0 программы, использующие DirectX, работать не будут.

    Runtime-часть DirectX поставляется вместе со многими приложениями DirectX. Она также имеется на CD-ROM, прилагаемом к этой книге. Распространяемые runtime-файлы DirectX предназначены только для Windows 95, не пытайтесь устанавливать их для Windows NT.

    Существует несколько способов узнать, установлена ли runtime-часть DirectX в вашей Windows 95. Во-первых, можно попытаться запустить любую демонстрационную программу, прилагаемую к этой книге или к DirectX SDK. Если программы работают, значит, runtime-часть установлена. Во-вторых, можно открыть панель управления и выбрать значок Add/Remove Programs. Если в открывшемся списке присутствует строка DirectX Drivers, значит, runtime-часть установлена. (Последний способ относится только к Windows 95. Хотя в Windows NT имеется встроенная поддержка DirectX, строка DirectX Drivers в диалоговом окне Add/Remove Programs не выводится). 

    СОВЕТ

    Новая возможность DirectX 5

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

    Необходимо также проверить версию имеющейся у вас runtime-части. Всего существуют четыре версии библиотеки: 1, 2, 3 и 5 (версии 4 не было). В каждой версии используется своя runtime-часть. Поскольку работа DirectX основана на спецификации COM с ее мощной поддержкой совместимости с предыдущими версиями, новые версии runtime-частей DirectX должны нормально работать со старыми приложениями DirectX. Чтобы узнать, какая версия библиотеки установлена на вашем компьютере, вызовите диалоговое окно Add/Remove Programs, выделите строку DirectX Drivers и нажмите кнопку Add/Remove — появляется список всех установленных компонентов. Компоненты с номерами версий, начинающимися с 4.02, принадлежат DirectX 1. Номера версий, начинающиеся с 4.03, относятся к DirectX 2, с 4.04 — DirectX 3, и 4.05 — DirectX 5. На прилагаемом к книге CD-ROM содержится runtime-часть от DirectX 5. 

    DirectX SDK 

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

    Найти DirectX SDK несколько сложнее, чем runtime-часть библиотеки. Этот пакет не входит в поставку Windows NT или Windows 95; его нет и на CD-ROM, прилагаемом к книге. Существуют три способа раздобыть SDK:

    • Купить Visual C++ 5.0 (в комплект которого входит DirectX 3 SDK).

    • Посетить на Web-узле Microsoft страницу для перекачки DirectX.

    • Подписаться на MSDN (Microsoft Development Network).

    Если у вас есть Visual C++, то есть и SDK. Хотя это не самая последняя версия, ее вполне хватит для большинства материалов из этой книги.

    SDK также можно получить на Web-узле Microsoft (адрес есть в предисловии). Объем пересылки оказывается довольно большим (из-за программ-примеров), поэтому приготовьтесь посвятить этому занятию целую ночь, особенно при модемном соединении.

    Третий способ вполне хорош, однако кое-кто считает нелогичным платить Microsoft за право разработки приложений, отчасти способствующих продвижению их операционных систем. SDK входит в MSDN уровня 2 и выше. 

    Visual C++ 

    После установки SDK необходимо сообщить Visual C++ о его местонахождении. По умолчанию SDK инсталлируется в каталог с именем dxsdk/sdk. Заголовочные файлы помещаются в каталог dxsdk/sdk/inc, а библиотечные — в каталог dxsdk/sdk/lib.

    Существуют два способа уведомить Visual C++ о местонахождении SDK. Вы можете либо указывать полный файловый путь при использовании файлов, либо включить нужные каталоги в путь поиска Visual C++. Желательно использовать второй метод, для чего применяется диалоговое окно, вызываемое командой Tools | Options | Directories. На рис. 1.1 показано, как выглядит это диалоговое окно после добавления пути к каталогу с заголовочными файлами. 

    Рис. 1.1. Диалоговое окно Visual C++ с перечнем каталогов 


    Вам также придется включить в список каталог dxsdk/sdk/lib. Это делается практически так же, как и с каталогом dxsdk/sdk/inc.

    На рисунке каталог DirectX был с помощью кнопок ­ и Ї помещен в начало списка, над стандартными каталогами Visual C++. Это имеет значение, если от версии DirectX SDK, входящей в Visual C++, вы перейдете к более свежей. В противном случае будет использоваться старая версия (Visual C++ просматривает каталоги из списка сверху вниз).

    После выполнения всех описанных действий вы сможете компилировать программы DirectX. Тем не менее осталась еще одна потенциальная проблема. Чтобы в DirectX 2 SDK нормально проходила компоновка программ, вызывающих функцию QueryInterface() для GUID интерфейсов DirectX, необходимо определить символическую константу INITGUID. Этот символ должен быть определен в одном и только одном исходном файле, и притом до самой первой директивы #include, как показано в следующем фрагменте:

    #define INITGUID

    #include <ddraw.h>

    // ... остальные директивы include ...

    В DirectX 3 и выше этот вариант тоже работает, однако появляется и более элегантное решение. Вместо того чтобы определять символ INITGUID, включите в проект файл dxguid.lib (в диалоговом окне, вызываемом командой Build | Settings). 

    Windows NT и Windows 95 

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

    И все же отличия существуют, особенно в части видеорежимов. Во-первых, Windows NT не поддерживает видеорежимы семейства Mode X. Независимо от того, поддерживаются ли такие видеорежимы установленной видеокартой, функция EnumDisplayModes() не сообщает о них, а функция SetDisplayMode() не активизирует их.

    Во-вторых, Windows 95 позволяет задавать параметры монитора, а Windows NT — нет. В Windows 95 список видеорежимов, обнаруженных DirectDraw, учитывает возможности как видеокарты, так и монитора. Если режим поддерживается только видеокартой, но не монитором, DirectDraw не включает его в список. Однако в Windows NT параметры монитора не задаются; следовательно, DirectDraw выведет список всех видеорежимов, поддерживаемых видеокартой, независимо от того, реализуются ли они установленным монитором.

    Следовательно, к переключению режимов в приложениях DirectDraw следует подходить с осторожностью. В коммерческих приложениях должен присутствовать механизм страховки, который бы позволял проверить видеорежим перед тем, как переходить в него. Подобный механизм используется при выборе видеорежима рабочего стола и в Windows NT, и в Windows 95. Когда вы приказываете Windows сменить видеорежим, новый режим активизируется на 15 секунд, после чего восстанавливается предыдущий. Затем диалоговое окно спрашивает, правильно ли система работала с новыми параметрами.

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

    Заключение  

    Наш интенсивный краткий курс DirectDraw подходит к концу. В главе 2 мы поговорим о проектировании и оптимизации приложений, а также о том, что ждет DirectDraw в ближайшем будущем.







     

    Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх