Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Гущин_SDL.pdf
Скачиваний:
168
Добавлен:
17.03.2018
Размер:
1.09 Mб
Скачать

void SDL_FreeSurface(SDL_Surface *surface);

Она освобождает ресурсы, используемые ранее созданной поверхностью, указанной единственным аргументом. При этом если поверхность была создана функцией SDL_CreateRGBSurfaceFrom, то данные об атрибутах пикселей поверхности (surface→pixels) не освобождаются, поскольку были выделены извне библиотеки SDL и структура surface не является их владельцем.

Пример, демонстрирующий возможные комбинации состояний флагов SDL_SRCCOLORKEY, SDL_SRCALPHA, значений полей format→Amask, format→colorkey и format→alpha

накладываемой и целевой поверхности и их влияние на результаты приведен в приложении 1.

В дополнение к рассмотренным функциям библиотека SDL позволяет создавать новые поверхности в памяти на основе внешнего файла изображения в формате Windows BMP или сохранять текущее состояние существующей во внешнем файле в том же формате. Для этого служат функции SDL_LoadBMP и SDL_SaveBMP:

SDL_Surface *SDL_LoadBMP(const char *file);

int SDL_SaveBMP(SDL_Surface *surface, const char *file);

Единственный параметр функции SDL_LoadBMP – указатель на начало последовательности байтов, заканчивающейся нулем и представляющей имя существующего файла изображения в фор-мате WindowsBMP в кодировке utf-8. Должно быть указано имя по правилам используемой операционной системы (либо полное имя, либо относительно текущего каталога, как правило – каталога с исполняемым файлом программы). При успешном выполнении функция возвращает указатель на созданную новую поверхность, формат и состояние пикселей которой определяются содержа-

нием файла изображения. В случае ошибки функция возвращает NULL.

Первый параметр функции SDL_SaveBMP – указатель на по-верхность, состояние которой следует сохранить в файле в фор-мате Windows BMP, второй параметр – указатель на начало после-довательности байтов, заканчивающейся нулем и представляющей имя создаваемого файла изображения в кодировке utf-8. Должно быть указано имя по правилам используемой

операционной

системы (либо полное

имя,

либо относительно

текущего

ката-

лога, как правило – каталога с исполняемым файлом программы). Если

такой файл

уже

существует,

то

он

будет

перезаписан.

При успешном выполнении функция возвращает 0, в случае ошибки – –1.

 

 

7. Имитация движения при выводе на дисплей

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

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

Самый простейший способ – рисование на мониторе всего изображения (кадра), задержка для его демонстрации, стирание и рисование следующего кадра с новым относительным расположением объектов.

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

Основным недостатком способа являются требования по быстродействию для обеспечения такой частоты смены кадров, которая бы не вызывала у человека ощущения мерцания экрана. В зависимости от разрешения и размера изображения приемлемой частотой смены кадров может

38

быть частота от 25 до 100 кадров в секунду. При этом если изображение формируется непосредственно в видеопамяти (непосредственно рисуется на экране), а аппаратная частота обновления экрана, по данным видеопамяти, превосходит требуемую частоту кадров, возможна ситуация, когда видеоадаптер за расчетное время отображения одного кадра отобразит несколько кадров, на части которых изображение в кадре будет еще не дорисовано до конца. Чтобы избежать такой ситуации, изображение кадра должно быть сначала полностью построено в некоторой отдельной области памяти и только потом перенесено в видеопамять. Поскольку в библиотеке SDL исходно используется принудительное обновление отображаемой области по готовности изображения, то для устранения влияния недо-строенных кадров достаточно корректно расставить вызовы функций обновления экрана и задержки для отображения. Приведем пример, показывающий реализацию способа с покадровой пере-рисовкой:

SDL_Surface *scr; /* Основная поверхность отображения */

void Draw_Background(SDL_Surface *surf); /* Рисует на поверхности surf неподвижный фон, всегда одинаковый

– реализация не приводится. При этом все предыдущее изображение удаляется */

void Draw_MovedObject(SDL_Surface *surf, Uint32 posnum); /* – Рисует на поверхности surf изображение некоторого движущегося объекта – один раз, в позиции номер posnum (пересчет номера позиции в конкретные координаты – зависит от конкретного изображения и не приводится,

как и остальная реализация */

void Draw_Frame(SDL_Surface *surf, Uint32 framenum) /* – Рисует на поверхности surf один кадр

из последовательности, показывающей движение объекта. Номер кадра передается вторым параметром framenum. Тело функции ниже: */

{

Draw_Background(surf); /* Фон одинаковый */ Draw_MovedObject(surf, framenum); /* На каждом кадре – новая позиция объекта *

}

/* Инициализация библиотеки SDL и создание поверхности scr не приводятся */

/* Цикл отображения ролика примерно на 1 минуту, всего 60 секунд * 25 кадров в секунду = 1500 кадров */

int framecnt = 0;

Uint32 before_next_frame = 40; /* 40 миллисекунд – задержка между перерисовкой кадров, если считать, что собственно рисование одного кадра ОЧЕНЬ быстрое */

/* Первый кадр – без задержек: */ Draw_Frame(sсr, framecnt++ ); /* Первый кадр */ while(framecnt < 1500)

{

SDL_Flip(scr); /* Отображение кадра на экране */

SDL_Delay(before_next_frame);

Draw_Frame(sсr, framecnt++ ); /* следующий кадр */

}

Альтернативой перерисовке всего кадра является перерисовка только его изменяющейся части, включающая два этапа: 1) восстановление фонового изображения на месте предыдущего изображения движущегося объекта; 2) рисование движущегося объекта в новом положении.

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

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

39

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

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

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

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

#include <stdlib.h> #include <SDL.h> #include <math.h> #include <SDL_draw.h>

const int scrwidth = 1027, scrheight = 768, scrdepth = 32; void draw_heli(SDL_Surface *surf, int centre_x, int centre_y,

int phase_big, int phase_samll)

{

const int lw = 200, lh = 80; if(surf)

{

if(surf->w >= centre_x + lw/2 && surf->h >= centre_y + lh/2 &&

centre_x - lw/2 >= 0 && centre_y - lh/2 >= 0)

{

SDL_Rect dstarea; double phase;

Sint16 x1, y1, x2, y2;

Uint32 keycolor = SDL_MapRGB(surf->format, 0, 255, 0); Uint32 helicolor = SDL_MapRGB(surf->format,

100, 100, 255);

Uint32 phasecolor = SDL_MapRGB(surf->format, 255, 100, 200);

int i;

dstarea.x = centre_x - lw/2; dstarea.y = centre_y - lh/2; dstarea.w = lw;

dstarea.h = lh;

SDL_FillRect(surf, &dstarea, keycolor); Draw_FillEllipse(surf, centre_x + 40, centre_y + 10, 60, 30, helicolor);

Draw_FillEllipse(surf, centre_x + 60, centre_y,

40

25, 15, keycolor); dstarea.x = centre_x + 35; dstarea.y = centre_y - 30; dstarea.w = 10;

dstarea.h = 15;

SDL_FillRect(surf, &dstarea, helicolor); dstarea.x = centre_x - 80;

dstarea.y = centre_y - 20; dstarea.w = 120;

dstarea.h = 10;

SDL_FillRect(surf, &dstarea, helicolor); dstarea.x = centre_x - 80;

dstarea.y = centre_y - 30; dstarea.w = 10;

dstarea.h = 30;

SDL_FillRect(surf, &dstarea, helicolor); for(i = 0; i < 15; i++)

{

phase = M_PI / 180 * (phase_samll + i) ;

x1 = floor(centre_x - 75 - (15 - i/2)*cos(phase)); y1 = floor(centre_y - 25 + (15 - i/2)*sin(phase)); x2 = floor(centre_x - 75 + (15 - i/2)*cos(phase)); y2 = floor(centre_y - 25 - (15 - i/2)*sin(phase)); Draw_Line(surf, x1, y1, x2, y2, phasecolor);

}

for(i = 0; i < 15; i++)

{

phase = M_PI / 180 * (phase_big + i) ; x1 = centre_x + 40;

y1 = centre_y - 30;

x2 = floor(x1 + (60 - i/2)*cos(phase));

y2 = floor(y1 - (7 - i/2)); Draw_Line(surf, x1, y1, x2, y2, phasecolor); x2 = floor(x1 - (60 - i/2)*cos(phase)); Draw_Line(surf, x1, y1, x2, y2, phasecolor);

}

}

}

}

void draw_sky(SDL_Surface *surf, int cloud_cnt)

{

int i;

Sint16 x0, y0; Uint16 xr, yr; Uint8 cl;

Draw_FillRect(surf, 0, 0, surf->w, surf->h, SDL_MapRGB(surf->format,0,200,255));

for(i = 0; i < cloud_cnt; i++)

{

xr = floor((rand()*0.05)/RAND_MAX * surf->w); yr = floor((rand()*0.05)/RAND_MAX * surf->h); x0 = xr + floor((rand()*1.0)/RAND_MAX *

(surf->w - 2*xr));

y0 = yr + floor((rand()*1.0)/RAND_MAX * (surf->h - 2*yr));

cl = 220 + floor((rand()*1.0)/RAND_MAX * 35); Draw_FillEllipse(surf, x0, y0, xr, yr,

SDL_MapRGB(surf->format, cl, cl, cl));

}

}

int main ( int argc, char** argv )

{

41

SDL_Surface *background, *temp, *sprites; int frame_num;

SDL_Rect frame_src, frame_dst, frame_tmp; if( SDL_Init( SDL_INIT_VIDEO ) < 0 )

{

printf( "Unable to init SDL: %s\n", SDL_GetError() ); return 1;

}

SDL_Surface* screen = SDL_SetVideoMode(scrwidth, scrheight, scrdepth, SDL_HWSURFACE|SDL_DOUBLEBUF);

if ( !screen )

{

printf("Unable to set 640x480 video: %s\n", SDL_GetError());

return 1;

}

background = SDL_CreateRGBSurface(SDL_HWSURFACE | SDL_DOUBLEBUF, scrwidth, scrheight, scrdepth, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);

temp = SDL_CreateRGBSurface(SDL_HWSURFACE | SDL_DOUBLEBUF, 200, 80, scrdepth, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);

sprites = SDL_CreateRGBSurface(SDL_HWSURFACE | SDL_DOUBLEBUF, 200, 80*36, scrdepth, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);

if( !(background && temp && sprites) )

{

printf("Unable to create temporary surfaces: %s\n", SDL_GetError());

return 1;

}

SDL_SetColorKey(sprites, SDL_SRCCOLORKEY, SDL_MapRGB(sprites->format, 0, 255, 0));

for(frame_num = 0; frame_num < 36; frame_num ++) draw_heli(sprites, 100, 40+80*frame_num,

10*frame_num, 10*frame_num); draw_sky(background, 250); SDL_BlitSurface(background, NULL, screen, NULL); int step = 0;

while (step < (50*60)) /* Примерно 1 минута */

{

frame_tmp.x = 0; frame_tmp.y = 0; frame_tmp.w = 200; frame_tmp.h = 80;

frame_dst.x = -200 + step % (scrwidth +200); frame_dst.y = scrheight/2 - 100; frame_dst.w = 200;

frame_dst.h = 80;

SDL_BlitSurface(screen, &frame_dst, temp, &frame_tmp); frame_src.x = 0;

frame_src.y = 80*(step % 36); frame_src.w = 200;

frame_src.h = 80;

SDL_BlitSurface(sprites, &frame_src, screen, &frame_dst); step++;

SDL_Flip(screen); SDL_Delay(20); /* 50 кадров/с */

SDL_BlitSurface(temp, &frame_tmp, screen, &frame_dst);

}

SDL_Quit();

42

Соседние файлы в предмете Программирование