netlib.narod.ru | < Назад | Оглавление | Далее > |
Последняя функция, вызываемая в ActivateDisplayMode() (см. листинг 3.2), — CreateCustomSurfaces(). Эта функция является чисто виртуальной, поэтому классы, производные от DirectDrawWin, должны реализовать ее. Функция CreateCustomSurfaces() создает вспомогательные поверхности, а также инициализирует переменные и структуры данных. В классе BounceWin эта функция выглядит так:
BOOL BounceWin::CreateCustomSurfaces() { CString filename; if (GetCurDisplayDepth() == 8) filename="tri08.bmp"; else filename="tri24.bmp"; if (surf1) surf1->Release(), surf1 = 0; surf1 = CreateSurface(filename, TRUE); if (surf1 == 0) { FatalError("failed to load BMP"); return FALSE; } return TRUE; }
В приложении Bounce используется одна вспомогательная поверхность, содержимое которой определяется одним из двух BMP-файлов: 8- или 24-битным. Функция использует 8-битный файл, если текущий видеорежим является 8-битным, и 24-битный файл — в противном случае. В принципе один и тот же BMP-файл можно использовать в обоих сценариях, но это неразумно. Поскольку 8-битные файлы могут состоять лишь из 256 цветов, не стоит использовать их в режимах High и True Color. В свою очередь 24-битные изображения не пользуются палитрами и могут содержать до 16 миллионов цветов. Генерация 256-цветной палитры для такого изображения становится нетривиальной задачей. Функция CreateCustomSurfaces() определяет глубину пикселей текущего видеорежима с помощью функции DirectDrawWin::GetCurDisplayDepth(). На основании полученного результата выбирается имя BMP-файла.
Затем мы проверяем указатель surf1. Если его значение отлично от нуля, поверхность освобождается, а указатель обнуляется. Это происходит из-за того, что функция CreateCustomSurfaces() может вызываться неоднократно. Функция ActivateDisplayMode() вызывает ее при активизации видеорежима, поэтому приложение, которое за время работы меняет несколько видеорежимов, несколько раз вызовет CreateCustomSurfaces(). Если поверхность создавалась ранее, приведенный код освобождает ее.
Затем мы вызываем функцию CreateSurface(), чтобы создать поверхность и загрузить в нее содержимое BMP-файла. Функция CreateSurface() есть в интерфейсе DirectDrawSurface, но в данном случае используется версия из класса DirectDrawWin. Функция CreateSurface() загружает BMP-файл и создает поверхность, размеры и содержимое которой определяются изображением, хранящимся в заданном BMP-файле. CreateSurface() возвращает указатель на созданную поверхность, если все прошло нормально, и ноль — в противном случае.
Обратите внимание на то, что функция CreateSurface() получает два аргумента. Первый из них представляет собой имя загружаемого BMP-файла. Второй аргумент показывает, нужно ли устанавливать палитру BMP-файла. Для 24-битных BMP-файлов этот аргумент игнорируется.
Наше приложение Bounce является очень простым, поэтому функция CreateCustomSurfaces() делает не так уж много. Реальное приложение может создавать десятки и даже сотни поверхностей. Класс DirectDrawWin содержит несколько служебных функций, которые могут пригодиться при работе с поверхностями, поэтому мы ненадолго отвлечемся от приложения Bounce и рассмотрим эти функции:
LPDIRECTDRAWSURFACE CreateSurface(LPCTSTR filename, BOOL installpalette = FALSE); LPDIRECTDRAWSURFACE CreateSurface(DWORD w, DWORD h ); BOOL LoadSurface(LPDIRECTDRAWSURFACE surf, LPCTSTR filename); BOOL ClearSurface(LPDIRECTDRAWSURFACE surf, DWORD clr, RECT* rect=0); BOOL ClearSurface(LPDIRECTDRAWSURFACE surf, DWORD r, DWORD g, DWORD b, RECT* rect = 0); BOOL GetSurfaceDimensions(LPDIRECTDRAWSURFACE surf, DWORD& w, DWORD& h);
Первая функция нам уже знакома. Функция CreateSurface(), получая имя BMP-файла, создает новую поверхность на основании его содержимого. Кроме того, эта функция может извлекать палитру из 8-битных файлов и назначать ее поверхности. Реализация этой функции подробно рассматривается в главе 5.
Вторая функция — CreateSurface() — создает поверхность заданных размеров. Эта функция полезна в тех случаях, когда вам нужна новая поверхность, содержимое которой не связано с BMP-файлом. Данная версия CreateSurface() реализована так:
LPDIRECTDRAWSURFACE DirectDrawWin::CreateSurface(DWORD w, DWORD h) { DWORD bytes = w * h * (displaydepth / 8); DDSURFACEDESC desc; ZeroMemory(&desc, sizeof(desc)); desc.dwSize = sizeof(desc); desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; desc.dwWidth = w; desc.dwHeight = h; desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; LPDIRECTDRAWSURFACE surf; HRESULT r = ddraw2->CreateSurface(&desc, &surf, 0); if (r == DD_OK) { TRACE("CreateSurface(%d,%d) created in video memory \n", w, h); return surf; } desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; r = ddraw2->CreateSurface(&desc, &surf, 0); if (r == DD_OK) { TRACE("CreateSurface(%d,%d) allocated in system memory \n", w, h); return surf; } TRACE("CreateSurface(%d,%d) failed\n", w, h); return 0; }
Функция CreateSurface() с помощью структуры DDSURFACEDESC описывает поверхность, размеры которой равны передаваемым параметрам w и h. Поле dwFlags показывает, какие поля структуры будут инициализироваться. Поля dwWidth и dwHeight определяют размеры поверхности, а поле dwCaps — ее возможности. Обратите внимание на флаг DDSCAPS_VIDEOMEMORY, согласно которому создаваемая поверхность должна находиться в видеопамяти.
Затем мы вызываем функцию DirectDraw CreateSurface(). В качестве первого аргумента передается указатель на структуру с описанием поверхности; если вызов окажется успешным, указатель surf будет ссылаться на созданную поверхность.
При успешном создании поверхности макрос MFC TRACE() выводит отладочное сообщение, а вы получаете указатель surf. Тем не менее раз мы явно указали, что поверхность должна находиться в видеопамяти, при нехватке последней вызов CreateSurface() может закончиться неудачно. В этом случае мы изменяем поле dwCaps, заносим в него флаг DDSCAPS_SYSTEMMEMORY и снова вызываем функцию CreateSurface(). Скорее всего, вторая попытка окажется успешной; если и на этот раз поверхность не будет создана, функция возвратит 0.
Но давайте вернемся к списку функций DirectDrawWin для работы с поверхностями. Функция LoadSurface() загружает содержимое BMP-файла в существующую поверхность. Эта функция будет часто упоминаться, когда речь пойдет о восстановлении потерянных поверхностей. Функция LoadSurface() похожа на первую версию CreateSurface() (с загрузкой BMP-файла).
Функции ClearSurface() могут использоваться для частичного заполнения поверхностей. Первая версия ClearSurface() заполняет поверхность величиной, передаваемой в качестве второго параметра. Необязательный аргумент rect определяет заполняемую прямоугольную область (если он не задан, заполняется вся поверхность). Вторая версия ClearSurface() получает в качестве аргументов RGB-составляющие и на их основании вычисляет значение, присваиваемое каждому пикселю поверхности. Из-за дополнительной работы, затрачиваемой на интерпретацию цветов, вторая версия работает медленнее первой. Первая функция ClearSurface() реализована так:
BOOL DirectDrawWin::ClearSurface(LPDIRECTDRAWSURFACE surf, DWORD clr, RECT* rect) { if (surf == 0) return FALSE; DDBLTFX bltfx; ZeroMemory(&bltfx, sizeof(bltfx)); bltfx.dwSize = sizeof(bltfx); bltfx.dwFillColor = clr; HRESULT r; r = surf->Blt(rect, 0, 0, DDBLT_COLORFILL | DDBLT_WAIT, &bltfx); return r == DD_OK; }
Функция ClearSurface() получает три аргумента: указатель на заполняемую поверхность; величину, присваиваемую каждому пикселю; и необязательную структуру RECT, которая определяет заполняемую область поверхности.
После проверки указателя мы подготавливаем экземпляр структуры DDBLTFX. Полю dwFillColor присваивается величина, используемая для заполнения, а сама операция осуществляется функцией Blt() интерфейса DirectDrawSurface. Флаг DDBLT_COLORFILL сообщает Blt() о том, что вместо блиттинга выполняется цветовое заполнение.
Получившаяся функция удобна, но ей не хватает универсальности. Дело в том, что величина, применяемая для заполнения поверхности, может иметь различный смысл. Например, для палитровых поверхностей она представляет собой индекс в палитре. Без предварительной проверки палитры невозможно предсказать, какой цвет будет использоваться для заполнения. Аналогичные проблемы возникают и для беспалитровых поверхностей, поскольку конкретное значение пикселя зависит от глубины и формата пикселей. Форматы пикселей особенно часто различаются в режимах High Color, поэтому заполнение поверхности конкретным цветом превращается в нетривиальную задачу.
Вторая версия ClearSurface() получает в качестве аргументов RGB-составляющие и рассчитывает по ним конкретную величину, присваиваемую пикселям поверхности. В таком варианте функция становится более универсальной, но и работает медленнее; быстродействие особенно сильно снижается для палитровых поверхностей, потому что нужный цвет приходится искать в палитре. Код этой функции будет рассмотрен в главе 5.
Нам остается рассмотреть лишь функцию GetSurfaceDimensions(), которая получает указатель на поверхность и возвращает ее ширину и высоту. Код этой функции выглядит так:
BOOL DirectDrawWin::GetSurfaceDimensions(LPDIRECTDRAWSURFACE surf, DWORD& w, DWORD& h) { if (surf == 0) return FALSE; DDSURFACEDESC desc; ZeroMemory(&desc, sizeof(desc)); desc.dwSize = sizeof(desc); desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT; if (surf->GetSurfaceDesc(&desc) != DD_OK) return FALSE; w = desc.dwWidth; h = desc.dwHeight; return TRUE; }
После проверки указателя мы подготавливаем экземпляр структуры DDSURFACEDESC. Нас интересуют ширина и высота поверхности, поэтому в поле dwFlags заносятся флаги DDSD_WIDTH и DDSD_HEIGHT.
Затем мы вызываем функцию GetSurfaceDesc() интерфейса DirectDrawSurface и передаем ей указатель на структуру с описанием поверхности. Функция GetSurfaceDesc() сохраняет размеры поверхности в полях dwWidth и dwHeight. Они присваиваются переданным по ссылке переменным w и h типа DWORD, после чего функция завершается.
Все рассмотренные функции встречаются в демонстрационных программах этой книги, однако вы вовсе не обязаны пользоваться ими. Впрочем, независимо от этого вам будет полезно познакомиться с их реализацией.
netlib.narod.ru | < Назад | Оглавление | Далее > |