Графика для Windows средствами DirectDraw


Выбор и отображение BMP-файла


Функция ShowDialog() вызывается при запуске приложения и при выборе нового файла. ShowDialog() подготавливает DirectDraw к отображению диалогового окна, выводит окно, получает информацию о выбранном BMP-файле и выбранном видеорежиме и отображает содержимое файла. Функция ShowDialog() приведена в листинге 5.7.

Листинг 5.7. Функция ShowDialog()


void BmpViewWin::ShowDialog() { CRect displayrect=GetDisplayRect(); if (displayrect.Width()<640) ddraw2->SetDisplayMode( 640, 480, 8, 0, 0 );

if (GetDisplayDepth()==8) { ClearSurface( backsurf, 0 ); primsurf->SetPalette( syspal ); } else { BltSurface( backsurf, bmpsurf, x, y ); }

ddraw2->FlipToGDISurface(); ShowCursor(TRUE);

if (bmpdialog==0) { bmpdialog=new BmpDialog(); bmpdialog->SetArrays( &palettemode, &nonpalettemode ); }

if (bmpdialog->DoModal()==IDCANCEL) { PostMessage( WM_CLOSE ); return; }

fullfilename=bmpdialog->fullfilename; filename=bmpdialog->filename; pathname=bmpdialog->pathname;

int index=bmpdialog->GetIndex(); DWORD w,h,d; if (bmpdialog->FilePalettized()) { w=palettemode[index].w; h=palettemode[index].h; d=palettemode[index].d; } else { w=nonpalettemode[index].w; h=nonpalettemode[index].h; d=nonpalettemode[index].d; }

if (GetDisplayDepth()==8) primsurf->SetPalette( palette );

ActivateDisplayMode( GetDisplayModeIndex( w, h, d) );

LoadBmp();

ShowCursor(FALSE); }




Функция ShowDialog() прежде всего проверяет, что текущий видеорежим имеет разрешение не менее 640x480. Из обсуждения функции SelectInitialDisplayMode() нам известно, что при инициализации приложения это условие заведомо выполняется, однако функция ShowDialog() также вызывается при каждом отображении BMP-файла. Если в данный момент установлен режим низкого разрешения, то перед тем, как продолжать, мы переходим в режим 640x480x8. Это обусловлено тем, что режимы низкого разрешения часто являются режимами Mode X, а GDI в таких режимах не может правильно отображать диалоговые окна.

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

СОВЕТ

Диалоговое окно и изображение

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

Вызов функции FlipToGDISurface() гарантирует, что вывод GDI будет присутствовать на экране. Кроме того, мы включаем курсор мыши (отключенный при запуске приложения классом DirectDrawWin), чтобы для работы с диалоговым окном можно было пользоваться мышью.

Далее мы создаем экземпляр класса BmpDialog, если он не был создан ранее. Класс-оболочка BmpDialog создается ClassWizard, он предназначен для отображения диалогового окна и работы с ним. Класс содержит код для работы с управляющими элементами окна и реакции на действия пользователя. Код класса BmpDialog здесь не рассматривается, так как он не имеет никакого отношения к DirectDraw.

Обратите внимание: при создании диалогового окна мы вызываем функцию SetArrays() и передаем ей массивы palettemode и nonpalettemode в качестве аргументов. Эта функция передает диалоговому окну информацию о видеорежимах, предназначенных для отображения как палитровых, так и беспалитровых изображений.

Диалоговое окно отображается функцией DoModal(). Пользователь сможет нажать кнопку Display лишь после того, как будет выбран BMP-файл и видеорежим. При этом мы сохраняем имя и путь выбранного BMP-файла и определяем параметры выбранного видеорежима.


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

Наконец, функция ActivateDisplayMode() активизирует выбранный видеорежим, функция LoadBmp() загружает содержимое BMP-файла, а курсор мыши отключается.

Чтобы лучше понять, как происходит загрузка файла, необходимо рассмотреть функцию LoadBmp(), которая не только загружает BMP-файл, но и инициализирует механизм прокрутки. Функция LoadBmp() приведена в листинге 5.8.

Листинг 5.8. Функция LoadBmp()

BOOL BmpViewWin::LoadBmp() { CWaitCursor cur;

LPDIRECTDRAWSURFACE surf; surf=CreateSurface( filename, TRUE ); if (surf) { if (bmpsurf) bmpsurf->Release(); bmpsurf=surf; } else { TRACE("failed to load new file\n"); return FALSE; }

displayrect=GetDisplayRect(); TRACE("display: %d %d\n", displayrect.right, displayrect.bottom); GetSurfaceRect( bmpsurf, bmprect ); TRACE("surface: %d %d\n", bmprect.right, bmprect.bottom);

int mx = displayrect.Width()-bmprect.Width(); if (mx<0) { xscroll=TRUE; xlimit=mx; x=0; } else { xscroll=FALSE; x=mx/2; }

int my = displayrect.Height()-bmprect.Height(); if (my<0) { yscroll=TRUE; ylimit=my; y=0; } else { yscroll=FALSE; y=my/2; }

update_screen=TRUE;

return TRUE; }


Сначала функция LoadBmp() создает объект MFC CWaitCursor, чтобы на время ее работы на экране отображался курсор Windows в виде песочных часов. Затем она вызывает функцию CreateSurface() и передает ей в качестве аргумента имя выбранного BMP-файла. Реализация CreateSurface() рассматривалась ранее в этой главе, поэтому мы знаем, что эта функция загружает указанный BMP-файл на новую поверхность.

Затем LoadBmp() определяет параметры новой поверхности и текущий активный видеорежим и использует полученные данные для инициализации переменных класса BmpViewWin, связанных с прокруткой и позиционированием поверхностей. Если размеры поверхности меньше размеров видеорежима, поверхность центрируется на экране; если поверхность больше, следует разрешить ее прокрутку.Переменные x и y определяют текущую позицию на поверхности, а переменные xlimit и ylimit ограничивают диапазон прокрутки. Логические переменные xscroll и yscroll показывают, разрешена ли горизонтальная и вертикальная прокрутка поверхности.

Наконец, логической переменной update_screen присваивается значение TRUE; оно говорит о том, что функция DrawScene() должна обновить первичную поверхность. О функции DrawScene() речь пойдет в следующем разделе.




Содержание раздела