Графика с использованием SDL. Урок 2. Загрузка и отображение изображений |
- Statistics
- Participants
- Translate into Russian
- Translation result
- Translated in draft, editing and proof-reading required. Completed: 9%.
Добро пожаловать в это небольшой урок, чей номер на единицу больше номера предыдущего урока. В этом уроке вы узнаете про загрузку и вывод на экран изображений в ваших SDL приложениях/играх/демках/и т.д. Я построчно пройду по исходному коду и попытаюсь объяснить что означает каждая его часть. Сегодня мы будем делать действительно простую программу: рисовать замечательный фон, а затем на нем создадим куб, который можно двигать с помощью клавиатуры.
Начнем с включения трех заголовочных файлов: stdio.h, stdlib.h и SDL.h (stdlib.h в дальнейшем используется функцией atexit()).
#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>
Далее мы объявляем три глобальные SDL_Surface. Глобальные значит, что они могут использоваться в любой функции данного исходного кода. Чтобы объявить глобальную переменную, она должна находиться в начале файла с исходным кодом. После SDL_Surface находятся еще две целочисленных переменных: xpos и ypos. Это координаты ящика.
SDL_Surface *back;
SDL_Surface *image;
SDL_Surface *screen;
int xpos=0,ypos=0;
Функция InitImages применяется для загрузки изображений из .bmp файлов (растровые изображения) в SDL_Surfaces. Она вызывается непосредственно в функции main(). Внутри InitImages используется функция SDL_LoadBMP, которой передается имя файла в качестве параметра. Она возвращает указатель на тип SDL_Surface. Мы загружаем два изображения в глобальные переменные типа SDL_Surface: bg.bmp, которое будет использоваться в качестве фонового, загружаем в переменную back, а image.bmp - в переменную image, это будет ящик.
int InitImages()
{
back = SDL_LoadBMP("bg.bmp");
image = SDL_LoadBMP("image.bmp");
return 0;
}
Затем идут две функции, которые будут применяться для рисования изображения на экране. Обе функции названы DrawIMG. Первая получает в качестве параметров SDL_Surface для рисования и координаты х и у. Для рисования изображения на экранной поверхности применяется функция SDL_BlitSurface() . Описание функции:
int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect);
здесь src - поверхность, которая будет перерисована на поверхность dst. Функция также получает два дополнительных параметра типа SDL_Rect: srcrect и dstrect. SDL_Rect - это структура, содержащая четыре 16-битных целых числа: x, y, w (width - ширина) и h (height - высота). srcrect указывает на копируемую область поверхности src. Если использовать NULL, то будет рисоваться все исходное изображение целиком. Координаты х и у параметра dstrect указывают на позицию, куда следует рисовать SDL_Surface src; ширина и высота игнорируются. Ниже приведена первая функция DrawIMG:
void DrawIMG(SDL_Surface *img, int x, int y)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;
SDL_BlitSurface(img, NULL, screen, &dest);
}
Вторая функция DrawIMG использует srcrect. Атрибуты x и y структуры srcrect говорят откуда начинать перерисовку, а w и h говорят сколько именно перерисовывать. Посмотрите на эту картинку:
ПРИМЕЧАНИЕ: изображение увеличено в 2 раза.
Предположим что тёмная область изображения это прямоугольник, который вы хотите передать в srcrect. Тёмная область начинается в точке 20x25 и её ширина 61 точка, а высота 70 точек. Если эта картинка (всё изображение, а не только тёмная часть) и будет той SDL_Surface которую требуется перерисовать, а x, y, w и h у srcrect будут 20, 25, 61 и 70, тогда произойдёт перерисовка тёмной области (картинки) на этой поверхности.
Вторая функция DrawIMG используется когда вы хотите перерисовать часть исходной поверхности на экран. Посмотрите на неё и попытайтесь понять что здесь происходит.
void DrawIMG(SDL_Surface *img, int x, int y,
int w, int h, int x2, int y2)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;
SDL_Rect dest2;
dest2.x = x2;
dest2.y = y2;
dest2.w = w;
dest2.h = h;
SDL_BlitSurface(img, &dest2, screen, &dest);
}
Теперь перейдём к функции DrawBG(). DrawBG вызывается из функции main перед тем как запускается главный цикл, и отображает красивый фон, на котором мы можем рисовать. ПРИМЕЧАНИЕ: чтобы перерисовать картинки на экране нам не требуется блокировка экрана потому что блокировать экран требуется ТОЛЬКО если мы хотим манипулировать пикселями напрямую (рисовать прямо на экране). Затем мы перерисовываем фоновое изображение на экран. Здесь мы не вызываем функцию обновления экрана (например SDL_Flip()) потому что эта функция используется только для того чтобы иметь красивый фон на котором можно рисовать. Мы не хотим чтобы пользователь видел чистое ничем не заполненное фоновое изображение в самом начале работы программы.
ПРИМЕЧАНИЕ: вы можете перерисовать SDL_Surface на любую другую SDL_Surface а не только на поверхность экрана. Вы можете изменить функции DrawIMG так чтобы они могли перерисовывать и на другие поверхности.
void DrawBG()
{
DrawIMG(back, 0, 0);
}
Затем идёт сама функция рисования сцены. Вначале мы рисуем ту часть фона, на которой сейчас находимся. Если этого не сделать, то "прямоугольник" который мы перемещаем по экрану будет оставлять за собой след. Мы не рисуем весь фоновый рисунок потому что это будет слишком медленно. Мы только рисуем часть фона, которой требуется перерисовка. Мы рисуем фон как прямоугольник, который начинается за 2 пикселя до и над (движущийся синий) прямоугольником и заканчивается 2 пикселя после и под прямоугольником. Так как (настоящий (синий)) прямоугольник может двигаться только на 1 пиксель в любом направлении за еденицу времени, мы уверены что перерисовка фона большего на 2 пикселя чем движущися прямоугольник в любом направлении, очистит след прямоугольника. Так как сам прямоугольник имеет размер 128х128 пикселей, тогда замена фона должна иметь размер (128+2+2)х(128+2+2)=132х132 пикселей. Закомментируйте первую (фоновую) функцию DrawIMG чтобы увидеть след прямоугольника. Вторая функция DrawIMG просто рисует прямоугольник в позиции x, y. Затем мы используем SDL_Flip для перемены буферов (т.е. обновления экрана).
void DrawScene()
{
DrawIMG(back, xpos-2, ypos-2, 132, 132, xpos-2, ypos-2);
DrawIMG(image, xpos, ypos);
SDL_Flip(screen);
}
И всё что остаётся сделать это main(). Вначале мы создаём указатель на тип "беззнакового 8-битного целого". Мы назовём этот указатель keys. Позднее keys будет использоваться для получения состояния клавиш клавиатуры в любой данный момент времени.
int main(int argc, char *argv[])
{
Uint8* keys;
Затем мы делаем стандартную инициализацию, о которой вы читали в последнем уроке.
if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )
{
printf("Инициализация SDL не удалась: %s\n", SDL_GetError());
exit(1);
}
atexit(SDL_Quit);
screen=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF);
if ( screen == NULL )
{
printf("Не удалось установить видеорежим 640x480: %s\n", SDL_GetError());
exit(1);
}
Далее проводим инициализацию изображений (загружаем в SDL_Surface), после чего рисуем фон. Рисование производится на поверхности SDL_Surface, а не непосредственно на экране.
InitImages();
DrawBG();
Мы запускаем главный цикл и привязываем выход из него к нажатию клавиши Escape.
int done=0;
while(done == 0)
{
SDL_Event event;
while ( SDL_PollEvent(&event) )
{
if ( event.type == SDL_QUIT ) { done = 1; }
if ( event.type == SDL_KEYDOWN )
{
if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; }
}
}
Теперь нужно получить статусы кнопок клавиатуры. Функция SDL_GetKeyState() возвращает указатель на массив типа Uint8, каждый элемент которого содержит информацию о клавише (нажата или нет). Так как события вызываются при нажатии клавиши, а не при ее удержании, то проверка статусов проводится вне цикла событий. Мы проверяем статусы кнопок со стрелами (влево, вправо, вверх, вниз), и если одна из них нажата, то передвигаем ящик в соответствующим направлении. В связи с тем, что можно одновременно нажимать несколько клавиш, проверка делается без использования "else if".
keys = SDL_GetKeyState(NULL);
if ( keys[SDLK_UP] ) { ypos -= 1; }
if ( keys[SDLK_DOWN] ) { ypos += 1; }
if ( keys[SDLK_LEFT] ) { xpos -= 1; }
if ( keys[SDLK_RIGHT] ) { xpos += 1; }
А теперь рисуем на экране.
DrawScene();
}
Original (English): GFX with SDL. Lesson 2: Loading and displaying images
