Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Direct Draw Термины и концепции - Пример реализации

Для начала необходимо подключить к проекту библиотеку ddraw.libи заголовочный файл ddraw.h Для использования DirectDraw необходимо создать ссылку на объект библиотеки DirectDraw (одновременно могут работать несколько приложений использующих эту библиотеку, поэтому прямое создание и удаление объектов DirectX недопустимо).

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

В демке я использовал только первичную поверхность, все остальные, в том числе видео-буфер я выделял "в ручную" фунцией new(). Это связанно с несколькими обстоятельствами. Во-первых у многих пользователей размер видеопамяти компьютера не привышает 1 мегабайта, и не справедливо было бы лешить их возможности, увидеть проектируемую игру. Во-вторых мои эксперименты с поверхностями показали, что на таком рядовом видеоадаптере как S3trio64V+ аппаратное ускорение практически отсутствует и необходимость в работе с поверхностями практически отпала. На мой взгляд, всегда лучше полностью контролировать работу программы самому, нежели отдовать часть работы другим разработчикам, т.к. намного легче чего-то изменить.Впрочем, в следующих своих поделках (имется ввиду движок игрухи) я буду использовать вторичный буфер прикрепленный к первичной поверхности, проще говоря странички всеже быстрее будут :) (поправка от 6.10.99 скорее все же буфер)

// Итак мы описываем два объекта:
LPDIRECTDRAW lpDD=NULL;             // DirectDraw объект
LPDIRECTDRAWSURFACE lpFront=NULL;   //DDraw первичная поверхность

Типы LPDIRECTDRAW и LPDIRECTDRAWSURFACE описаны в подключенном заголовочном файле ddraw.h Далее нам необходимо написать функцию, которая создаст объект DirectDraw и установит нужный режим работы. До вызова этой функции мы должны проделать такие необходимые, при программировании под Windows, вещи как регистрация класса окна и создание главного окна приложения, после чего в нашем распоряжении будет необходимая переменная- дескриптор окна HWND. Так как мы не собираемся создавать вторичные поверхности, функция будет очень простой.
Необходимо заметить, что обмен параметрами с функциями DirectX часто идет через заполнение структур, которых в DirectX просто ужасающее количество.

// Создает главный объект DD , устанавливает режим работы
BOOL ddInit(HWND hwnd, HINSTANCE hInst)
{ 
  HRESULT result;     // возвращаемое значение
  DDSURFACEDESC ddsd; //структура, описывающая поверхность

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

  result = DirectDrawCreate( NULL, &lpDD, NULL );
  if( result!=DD_OK ) goto dderror;

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

  result = lpDD->SetCooperativeLevel( hwnd, DDSCL_EXCLUSIVE |
  DDSCL_FULLSCREEN );
  if( result!=DD_OK ) goto dderror;

Если все нормально то продолжим работу. И установим нужный нам видео режим. У меня его задают соответствующие константы ширина, высота, глубина цвета. Вызов функции опять же через объект DirectDraw. Замечание: здесь приводится описание интерфейса DirectDraw, а существует еще DirectDraw2, и т.д. DirectDraw2, например позволяет переключать не только разрешение, но и частоту вертикального развертки монитора.

  result = lpDD->SetDisplayMode( SCREEN_WIDTH, SCREEN_HEIGHT,
  SCREEN_DEPTH);
  if( result!=DD_OK ) goto dderror;

Если все пока нормально то… Создадим первичную поверхность с одним буфером. Для этого заполним структуру работы с поверхностью. Вначале всегда необходимо обнулить содержимое структуры. Кстати размер структур в windows вообще не постоянен, поэтому рекомендуется использовать метод sizeof для определения занимаемого структурой места.

  memset(&ddsd,0,sizeof( ddsd ));
  ddsd.dwSize = sizeof( ddsd );
  ddsd.dwFlags = DDSD_CAPS;
    // в структуре используются другие структуры,
    // об изменении которых необходимо проинформировать вызываемую
    // функцию.

  ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
    // указали, что это первичная поверхность
    // И теперь вызываем функцию для создания поверхности.
  result = lpDD->CreateSurface( &ddsd, &lpFront, NULL );
  if( result!=DD_OK ) goto dderror;
    //Теперь все необходимое проделали и можно выходить из функции.
  return TRUE;
    //Если произошла ошибка, проинформируем об этом пользователя
    // и выйдем.
dderror:
  MessageBox( hwnd, "Direct Draw Init Failed", "ERROR", MB_OK ); 
  ddRelease();
  return FALSE;
}

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

// освобождает объекты DirectDraw1 и DirectSurface
void ddRelease()
{ 
  if( lpDD != NULL )
    {
      if( lpFront != NULL )
        {
          lpFront>Release(); lpFront = NULL;
        }
      lpDD>Release(); lpDD = NULL;
    }
}

Удаление функцией delete не допустимо, т.к. нельзя забывать, что мы работаем в многозадачной среде, и возможно, что другое приложение тоже использует DirectX. Функция Release просто уменьшает счетчик созданных объектов и при достижении им нуля, объект сам удаляется из памяти.
Все это хорошо, но пока совсем не понятно как работать с поверхностью, которая может плавать по памяти и вообще находиться непонятно где. Для решения этой проблемы придумали специальный метод объекта поверхность, который назвали блокировкой поверхности. Вмести с ним, используется обратный метод- разблокировка, позволяющий поверхности свободно плавать. Можно заблокировать отдельную часть поверхности, указав ее в качестве одного из параметров как прямоугольную область. В литературе метод блокировки используется в основном во время вызова внутренних функций DirectDraw для работы с поверхностью. Я же его использую с единственной целью получить адрес битовой карты поверхности (собстенно мы подошли к тому моменту из-за чего все затевалось).
В качестве примера приведу исходник функции, копирующей картинку в формате .spr или .pct (мои собственные форматы, созданные для удобства) на первичную поверхность или любую другую поверхность.
Функция принимает следующие параметры: lps-указатель на поверхность, rect-рамка по которой необходимо обрезать выводимую картинку, psrc -объект картинка, X,Y-координаты вывода картинки относительно верхнего - левого угла поверхности, dark и light - коэффициенты затемнения и осветления картинки.


void picPutS(LPDIRECTDRAWSURFACE lps, RECT& rect, PIC& psrc,
long X, long Y, long dark, long light)
{ 
  if(psrc.data==NULL) return;       // обычная проверка
  DDSURFACEDESC ddsd;               // структура для работы с поверхностью
  HRESULT result;                   // возвращаемый результат 

  memset(&ddsd,0,sizeof(ddsd)); // обнулим и заполним структуру
  ddsd.dwSize=sizeof(ddsd);

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

  result=lps->Lock(NULL, &ddsd, DDLOCK_WAIT,NULL);

Если все нормально, то функция Lock помимо блокировки поверхностти заполнила структуру ddsd конкретными характеристиками поверхности.

if(result==DD_OK)
  {

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

    // ddsd.dwWidth -ширина поверхности
    // ddsd.dwHeight- высота поверхности
    // ddsd.lpSurface- адрес битового массива поверхности 
    PIC desc;
    desc.type=PIC_pct | PIC_16;
    desc.width=ddsd.dwWidth;
    desc.height=ddsd.dwHeight;
    desc.data=(char*)ddsd.lpSurface;

Далее просто вызывается обычная функция для копирования изображения, которая ничего не подозревает о существовании DirectX вообще и DirectDraw в частности.

    picPut(desc,rect,psrc,X,Y,dark,light);
    // Остается только разблокировать поверхность.
    lps->Unlock(0);
  }
}

Критическое замечание:

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

double2:
result=lpFront->IsLost();
if(result==DDERR_SURFACELOST)
{
   lpFront->Restore(); goto double2;
}

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог