DOSUG CZ– розовая кнопка на сайте!
Logo

Расширяем наши знания о DirectX (примеры DDEX2 и DDEX3)

(первую статью см. "Основы программирования игр с использованием DirectDraw")

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

Второй пример в DirectX 5 SDK (DDEX2) является дальнейшим развитием приложения DDEX1. DDEX2 включает возможность загрузки битмапа в бэк-буфер.

Третий пример DirectDraw еще более функционален. DDEX3 создает две дополнительные поверхности и загружает битмап на каждую из них. Затем используется метод IDirectDrawSurface::BltFast для копирования содержимого одной из дополнительных поверхностей в бэк-буфер, затем с помощью операции флиппинга бэк-буфер отображается на экране, основная поверхность становится бэк-буфером, и на нее копируется содержимое второй дополнительной поверхности.

В следующем разделе функционирование этих примеров будет рассмотрено более подробно.

Загрузка битмапа на поверхность

Так же, как и в DDEX1, doInit содержит код инициализации приложения DDEX2. Код инициализации DirectDraw выглядит абсолютно также, как и в DDEX1, за исключением следующих строк:

lpDDPal = DDLoadPalette(lpDD, szBackground);
 if (lpDDPal == NULL)
        goto error;
 ddrval = lpDDSPrimary->SetPalette(lpDDPal);
 if( ddrval != DD_OK )
        goto error;
 // Load a bitmap into the back buffer.
 ddrval = DDReLoadBitmap(lpDDSBack, szBackground);  
 if( ddrval != DD_OK ) 
        goto error;  

Создание палитры

Первая строка кода вызывает функцию DDLoadPalette. Если вам хочется посмотреть на прототип функции DDLoadPalette, вы можете найти ее в файле Ddutil.cpp, находящемся в каталоге \DXSDK\SDK\SAMPLES\MISC. Как вы увидите, Ddutil.cpp используется большинством примеров DirectDraw из DirectX 5 SDK. В этом файле содержатся подпрограммы для загрузки битмапов и палитр из файлов ресурсов.

Замечание   Если вы используете Microsoft Developer Studio для компиляции примера DDEX2 и других примеров, входящих в поставку DirectX 5 SDK, то вам нужно включить файл Ddutil.cpp в список файлов рабочего пространства DDEXx.

Для включения Ddutil.cpp в рабочее пространство:

  1. В меню Project выберите пункт Add To Project и затем Files.
  2. Нажмите Browse.
  3. Кликните на каталоге DXSDK\SDK\SAMPLES\MISC.
  4. Выделите Ddutil.cpp.
  5. Нажмите OK.

В примере DDEX2 функция DDLoadPalette создает объект DirectDrawPalette из файла Back.bmp. Функция DDLoadPalette проверяет, существует ли файл или ресурс. Если файла не существует, создается палитра по умолчанию. В нашем случае функция извлекает из битмапа информацию о палитре и сохраняет ее в структуре, адресуемой с помощью переменной ape. Затем создается объект DirectDrawPalette как показано в следующем коде:

pdd->CreatePalette(DDPCAPS_8BIT, ape, &ddpal, NULL);
return ddpal;  

После вызова метода IDirectDraw::CreatePalette параметр ddpal указывает на объект DirectDrawPalette, который используется в качестве возвращаемого значения функцией DDLoadPalette.

Параметр ape - указатель на структуру, которая может содержать 2, 4, 16, или 256 элементов, организованных линейно. Количество элементов зависит от параметра dwFlags, передаваемого функции IDirectDraw::CreatePalette. В нашем случае dwFlags имеет значение DDPCAPS_8BIT, что означает, что структура будет содержать 256 элементов. Каждый элемент занимает 4 байта (канал красного, канал зеленого, канал синего и байт флагов).

Установка палитры

После создания палитры можно передать указатель на объект DirectDrawPalette (ddpal) основной поверхности, вызвав метод IDirectDrawSurface::SetPalette как показано в следующем примере:

ddrval = lpDDSPrimary->SetPalette(lpDDPal);
 if( ddrval != DD_OK )
    // SetPalette failed.  

После вызова IDirectDrawSurface::SetPalette объект DirectDrawPalette "прикреплен" к объекту DirectDrawSurface. В любой момент, когда вам нужно сменить палитру все, что нужно сделать - создать новую палитру и переустановить ее. (Так сделано в этом примере. Есть другие способы изменения палитры, мы их разберем в следующих примерах.)

Загрузка битмапа на бэк-буфер

После вышеописанных операций DDEX2 загружает Back.bmp на бэк-буфер используя следующий код:

// Загружаем битмап на бэк-буферr. 
 ddrval = DDReLoadBitmap(lpDDSBack, szBackground);
 if( ddrval != DD_OK )
    // Загрузка неудачна.  

DDReLoadBitmap - еще одна функция из файла Ddutil.cpp. Она загружает битмап из файла на уже существующую поверхность DirectDraw. (Можно использовать DDLoadBitmap для создания поверхности и загрузки на нее битмапа как это будет показано в DDEX5.) В примере DDEX2 функция загружает файл Back.bmp на бэк-буфер, адресуемый указателем lpDDSBack. DDReLoadBitmap вызывает метод DDCopyBitmap для копирования картинки на бэк-буфер и растягивания ее до нужных размеров.

Функция DDCopyBitmap копирует битмап в память, затем использует функцию GetObject для определения размера картинки. Затем используется следующий код для определения размера бэк-буфера, на который мы помещаем битмап:

// 
 // Получение размера буфера. 
 // 
 ddsd.dwSize = sizeof(ddsd); 
 ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH; 
 pdds->GetSurfaceDesc(&ddsd);  

ddsd - указатель на структуру DDSURFACEDESC. В этой структуре хранится описание поверхности DirectDraw. В нашем случае нас интересуют поля DDSD_HEIGHT и DDSD_WIDTH, которые содержат высоту и ширину бэк-буфера. Вызов метода IDirectDrawSurface::GetSurfaceDesc заполняет структуру правильными значениями. В примере DDEX2 эти значения будут равны 480 и 640 (высота и ширина соответственно).

Функция DDCopyBitmap блокирует поверхность и копирует битмап на бэк-буфер, растягивает или сжимает ее используя процедуру StretchBlt:

if ((hr = pdds->GetDC(&hdc)) == DD_OK) 
 {
    StretchBlt(hdc, 0, 0, ddsd.dwWidth, ddsd.dwHeight, 
               hdcImage, x, y,  dx, dy, SRCCOPY);
    pdds->ReleaseDC(hdc); 
 }  

Флиппинг поверхностей

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

Блиттинг из неотображаемой поверхности

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

Создание неотображаемых поверхностей

Следующий фрагмент кода добавлен к функции doInit примера DDEX3 для создания дополнительных неотображаемых поверхностей:

// Создание битмапа. 
 ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; 
 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; 
 ddsd.dwHeight = 480; 
 ddsd.dwWidth = 640; 
 ddrval = lpDD->CreateSurface( &ddsd, &lpDDSOne, NULL ); 
 if( ddrval != DD_OK ) 
 {
    return initFail(hwnd); 
 }  
// Создание другой картинки. 
ddrval = lpDD->CreateSurface( &ddsd, &lpDDSTwo, NULL ); 
if( ddrval != DD_OK ) 
{
   return initFail(hwnd); 
} 

Как видно из приведенного фрагмента, поле dwFlags определяет, что в приложении будет использоваться структура DDSCAPS и что мы будем устанавливать высоту и ширину поверхности. Поверхность будет неотображаемым (off-screen) буфером, что определяется флагом DDSCAPS_OFFSCREEN, установленным в структуре DDSCAPS. Высота и ширина в структуре DDSURFACEDESC устанавливается соответственно в 480 и 640. Затем поверхность создается методом IDirectDraw::CreateSurface.

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

При создании буфера можно указать, где он будет создан - в системной памяти или памяти видеоадаптера. Делается это установкой флагов DDSCAPS_SYSTEMMEMORY или DDSCAPS_VIDEOMEMORY в структуре DDSCAPS. Помещая буфер в видеопамять, вы тем самым увеличиваете скорость копирования информации между бэк-буфером и неотображаемой поверхностью. Подробнее на этом мы остановимся позже, при обсуждении вопроса об анимации. Как бы то ни было, вы должны знать, что указывая флаг DDSCAPS_VIDEOMEMORY для неотображаемого буфера вы рискуете получить ошибку DDERR_OUTOFVIDEOMEMORY если для создания поверхности не хватает памяти видеокарты.

Загрузка битмапов на неотображаемые поверхности

После создания двух дополнительных неотображаемых поверхностей DDEX3 использует подпрограмму InitSurfaces для загрузки изображений, хранящихся в файле Frntback.bmp на поверхности. Подпрограмма InitSurfaces в свою очередь вызывает функцию DDCopyBitmap, находящуюся в файле Ddutil.cpp для загрузки обеих картинок:

// Загружаем ресурсы. 
 hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), 
        szBitmap, IMAGE_BITMAP, 0, 0,  LR_CREATEDIBSECTION);
 if (hbm == NULL) 
    return FALSE;   
 DDCopyBitmap(lpDDSOne, hbm, 0, 0,   640, 480); 
 DDCopyBitmap(lpDDSTwo, hbm, 0, 480, 640, 480); 
 DeleteObject(hbm);
 return TRUE;  

Если вы загрузите файл Frntback.bmp в Microsoft Paint или другой графический редактор, вы увидите, что изображение состоит из двух картинок, одна над другой. Функция DDCopyBitmap разбивает изображение на две картинки и загружает одну из них на первую поверхность (lpDDSOne), а вторую картинку - на вторую неотображаемую поверхность (lpDDSTwo).

Блиттинг неотображаемой поверхности на бэк-буфер

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

    rcRect.left = 0;
    rcRect.top = 0;
    rcRect.right = 640;
    rcRect.bottom = 480;
    if(phase)
    {
        pdds = lpDDSTwo;
        phase = 0;
    }
    else
    {
        pdds = lpDDSOne;
        phase = 1;
    }
    while( 1 )
    {
        ddrval = lpDDSBack->BltFast( 0, 0, pdds, &rcRect, FALSE );
        if( ddrval == DD_OK )
        {
            break;
        }
 

Перменная phase определяет, какая поверхность копируется в бэк-буфер. Затем вызывается метод IDirectDrawSurface::BltFast для запуска операции блиттинга начиная с позиции 0, 0, верхнего левого угла. Параметр rcRect указывает на структуру RECT которая определяет верхний левый и правый нижний углы участка неотображаемой поверхности, который будет скопирован. Последний параметр, установленный в FALSE (или 0), показывает, что не указываются специфичные флаги копирования.

Хотелось бы добавить несколько слов о том, какой из методов выбрать - IDirectDrawSurface::Blt или IDirectDrawSurface::BltFast. Если происходит копирование из поверхности, которая расположена в памяти видеоадаптера, то лучше использовать IDirectDrawSurface::BltFast. Хотя вы не получите выигрыша в скорости на аппаратуре, использующей аппаратный блиттинг, однако на видеоадаптерах, где применена эмуляция апппаратного блиттинга можно получить до 10 процентов прироста производительности. Поэтому желательно применять метод IDirectDrawSurface::BltFast для всех операций блиттинга, происходящих из памяти видеоадаптера в такую же память. Если копирование происходит из системной памяти или используются специфичные флаги, то лучше использовать IDirectDrawSurface::Blt.

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

   
главная - о проекте - контакты - реклама на сайте
 
LBN100 Elite

SoftStudio.Ru - студия разработки программ
LBN100 Elite