Графика для Windows средствами DirectDraw
Шрифт:
Обратите внимание: поверхность изображения копируется непосредственно на первичную поверхность, как говорилось при обсуждении структуры программы Smear.
Перед завершением приложения MFC вызывает функцию OnDestroy; мы воспользуемся ею для освобождения объектов DirectInput. Функция OnDestroy выглядит так:
Функция OnDestroy
В этой главе мы узнали, как организовать в своих приложениях поддержку DirectInput и обойти традиционные механизмы ввода Windows. Такое решение повышает быстродействие и дает больше возможностей для обработки пользовательского ввода. По этим причинам DirectInput будет использоваться во всех оставшихся программах этой книги.
В следующей главе мы займемся одной неприятной проблемой, связанной с курсором мыши. Как вы вскоре убедитесь, DirectInput станет неотъемлемой частью ее решения.
Глава 7. Проблема курсора
Если вы пытались работать с мышью в полноэкранном приложении DirectDraw, скорее всего, проблема с курсором вам уже знакома. Ввод от мыши нетрудно получить и использовать в программе, пока не приходится отображать курсор на экране. Но попробуйте-ка вывести стандартный курсор Windows — и проблема заявит о себе.
Первая трудность связана с переключением страниц. Если курсор мыши не был отключен, Windows не подозревает о том, что видеоустройства находятся под управлением DirectDraw, и выводит курсор мыши на поверхности GDI. Если поверхность GDI скрыта, а вместо нее на экране отображается другая поверхность, курсор исчезает. Эта переменчивость приводит к тому, что курсор мыши мерцает и выглядит полупрозрачным.
Более того, курсор может оказаться искаженным или вообще отсутствовать. Например, в видеорежимах Mode X используется нелинейная организация пикселей. Windows пытается вывести курсор мыши, но поскольку режимы Mode X не поддерживаются Windows GDI, изображение курсора портится. Кроме того, Windows не умеет выводить курсор мыши на вторичных видеоустройствах (например, на видеокартах с чипами 3Dfx). Вывод продолжает поступать на первичное видеоустройство независимо от того, какое устройство активно в данный момент, поэтому курсор мыши пропадает.
Значит, если полноэкранное приложение захочет вывести курсор мыши, ему придется делать это самостоятельно. На первый взгляд все просто: нужно создать небольшую поверхность с изображением курсора и перемещать ее в соответствии с вводом от мыши. Такое решение работает, но у него есть свои недостатки.
Раз курсор мыши отображается самим приложением, а не Windows, частота его прорисовки
Существует множество причин, по которым приложение может иметь низкую частоту вывода кадров. Трехмерные приложения предъявляют особенно жесткие требования к системе и часто работают с частотой 30 FPS и менее. Но «тормозить» могут и обычные, не трехмерные приложения. Сложное приложение, выводящее сотни объектов, будет иметь низкий FPS (конечно, все зависит от компьютера и видеокарты). Кроме того, режимы High и True Color часто оказываются намного медленнее 8-битных режимов.
Более того, простому приложению DirectDraw вряд ли потребуется курсор мыши. Если возникла необходимость в курсоре, значит, пользователь должен выделять определенные области экрана. Сомнительно, чтобы сложное приложение смогло обеспечить высокую частоту вывода, пока пользователь работает с мышью.
Итак, нам придется искать нетривиальное решение. Оно должно обеспечивать быструю реакцию на действия с мышью независимо от FPS, а курсор не должен мерцать. Поскольку нам все равно придется создавать собственный курсор, для него можно выбрать произвольный размер.
Эта глава посвящена решению, которое удовлетворяет всем перечисленным критериям. Сначала мы обсудим частичное обновление экрана (прямой блиттинг на первичную поверхность), а затем поговорим о том, как многопоточность обеспечивает обновление курсора, не зависящее от частоты вывода. Наконец, теория воплотится на практике в виде программы Cursor.
Частичное обновление экрана
Типичное приложение DirectDraw (наподобие тех, что рассматривались в предыдущих главах) заранее строит весь кадр во вторичном буфере и затем переключает страницы. Эта методика работает быстро (переключение страниц обычно происходит почти мгновенно) и не вызывает мерцания (построение каждого кадра завершается до его вывода).
Чтобы обновление курсора не зависело от частоты вывода, нам придется обновлять курсор так, чтобы обойтись без переключения страниц. Поэтому вместо того, чтобы обновлять весь экран, мы будем перерисовывать лишь его часть. Для этого можно непосредственно изменить содержимое первичной поверхности.
Хотя в нашем случае частичное обновление экрана используется для вывода курсора мыши, эта методика полезна и в других случаях. Например, приложение может обновить меню прямо на первичной поверхности вместо того, чтобы заново строить весь кадр (вместе с изменившимся меню) на вторичном буфере и затем переключать страницы.
С другой стороны, прямой вывод на первичную поверхность имеет свои недостатки и требует осторожности. Основная потенциальная проблема — расхождение. Переключение страниц выполняется так, чтобы предотвратить возможность расхождения. Следовательно, если вы обходите механизм переключения страниц и обновляете кадр, который в данный момент отображается на экране, то рискуете изменить область экрана, в данный момент обновляемую монитором. Если это произойдет, новое содержимое обновляемой части в течение некоторого времени будет выводиться одновременно со старым — возникнет расхождение.