Форум программистов, компьютерный форум, киберфорум
Rem_
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  

SDL2 разделение функций, вывод изображения.

Запись от Rem_ размещена 24.08.2015 в 09:22
Показов 7588 Комментарии 0

Мы научились открывать окно. Теперь выведем на него рисунок.
В оригинальном уроке автор теперь будет размещать только главные части кода.
Сам урок тут - http://lazyfoo.net/tutorials/S... /index.php
Архив с исходниками тут - http://lazyfoo.net/tutorials/S... screen.zip
Открывайте сразу код, он на 120 строк, и давайте изучать.

В первом уроке всё помещено в main функцию, так как программа маленькая. В реальных программах код разбивается на модули. Это позволит создавать отдельные части, каждую из которых можно независимо исправлять и изменять.
Создадим отдельные функции для инициализации, для загрузки изображения, и для закрытия sdl. Объявляем в верху исходника.
C
1
2
3
bool init();
bool loadMedia();
void close();
Теперь объявляем глобальные переменные. Обычно стараются избегать глобальных, но мы делаем программу как можно проще, всё находится в одном файле, поэтому оставим так.
Новый тип переменной - SDL_Surface - это просто тип данных изображения, в котором содержится информация о пикселях. SDL_Surface использует программный рендер. Это значит что для рендера будут использоваться ресурсы процессора (CPU). Можно рендерить используя аппаратные средства, но это чуть сложнее и будет рассмотрено позже на примере с использованием графической карты (GPU).
Изображение будет загружаться из файла и отображаться в окне.

Обратите внимание, что это SDL_Surface* указатель на поверхность. Для чего так сделано?
  1. Динамическое использование памяти для загрузки изображения
  2. Удобное обращение к изображению в памяти. Например, для отрисовки нескольких кирпичей. Намного проще держать в памяти одно изображение и использовать его несколько раз снова и снова.
И не забывайте после объявления сразу инициализировать значением NULL:
C
1
2
3
SDL_Window* gWindow = NULL;
SDL_Surface* gScreenSurface = NULL;
SDL_Surface* gHelloWorld = NULL;
Функцию инициализации и создания окна выносим в отдельную функцию. Мы вызываем SDL_GetWindowSurface, чтоб получить сёрфейс нашего окна. Именно туда мы будем выводить изображение.
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool init()
{
    bool success = true;
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
        success = false;
    }
    else
    {
        gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
        if( gWindow == NULL )
        {
            printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
            success = false;
        }
        else
        {
            gScreenSurface = SDL_GetWindowSurface( gWindow );
        }
    }
    return success;
}
В функции загрузки изображения загружаются файлы используя SDL_LoadBMP. По указанному адресу берётся .bmp файл и помещается в указанную поверхность. Возвращает указатель на эту поверхность. Если вернуло NULL значит произошла ошибка и текст ошибки выведется на консоль.
C
1
2
3
4
5
6
7
8
9
10
11
bool loadMedia()
{
    bool success = true;
    gHelloWorld = SDL_LoadBMP( "02_getting_an_image_on_the_screen/hello_world.bmp" );
    if( gHelloWorld == NULL )
    {
        printf( "Unable to load image %s! SDL Error: %s\n", "02_getting_an_image_on_the_screen/hello_world.bmp", SDL_GetError() );
        success = false;
    }
    return success;
}
В данном примере в папке проекта, в папке "02_getting_an_image_on_the_screen" ищется файл с именем "hello_world.bmp". Если программа не может найти изображение, проверяйте внимательнее адреса.

В завершающей функции нужно закрыть окно и выйти из SDL. Но перед этим нужно освободить память от поверхностей SDL_FreeSurface. Поверхность окна освободится при функции SDL_DestroyWindow.
Возьмите в привычку присваивать указателям значение NULL, когда они пока ни куда не указывают.
C
1
2
3
4
5
6
7
8
9
10
void close()
{
    SDL_FreeSurface( gHelloWorld );
    gHelloWorld = NULL;
 
    SDL_DestroyWindow( gWindow );
    gWindow = NULL;
 
    SDL_Quit();
}
Теперь main функция. В ней вызываем инициализацию sdl и загрузку изображения. Если всё пройдёт успешно, то изображение из загруженной поверхности выведется на оконную поверхность используя SDL_BlitSurface.
Что делает блиттинг (blitting)? Он берёт исходную поверхность (source surface) и переносит на целевую поверхность (destination surface). Первый аргумент функции SDL_BlitSurface - поверхность источник, третий аргумент - конечная поверхность. Второй и четвертый будут рассмотрены позже.
Начало main функции:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main( int argc, char* args[] )
{
    if( !init() )
    {
        printf( "Failed to initialize!\n" );
    }
    else
    {
        if( !loadMedia() )
        {
            printf( "Failed to load media!\n" );
        }
        else
        {
            SDL_BlitSurface( gHelloWorld, NULL, gScreenSurface, NULL );
В нашем примере всего одна функция для рисования. Но изображения ещё не видно. Нужно сделать ещё шаг.
C
1
SDL_UpdateWindowSurface( gWindow );
Обычно, пока вы последовательно прорисовываете то, что нужно в данном кадре, вы этого не видите. Вам нужно обновить экран используя SDL_UpdateWindowSurface. Большинство систем рендеринга используют двойную буферизацию.
Когда вы рисуете используя SDL_BlitSurface, то рендер происходит в теневом буфере. На экране в этот момент показывается то, что находится в "переднем" буфере. Так делается, потому что в игре нужно нарисовать множество объектов. Если рисовать только во фронтальном буфере, то всё будет мигать, пока не нарисуется полный кадр. Поэтому сначала всё рисуется в теневом буфере, а затем мгновенно буферы меняются местами и человек видит полностью нарисованный кадр.
Не нужно вызывать SDL_UpdateWindowSurface после каждого блиттинга. Только после всех процедур рисования и блиттинга достаточно вызвать обновление, один раз для каждого кадра.

Вот мы всё нарисовали. Делаем задержку в 2 секунд, после чего закрываем программу
C
1
2
3
4
5
6
7
8
9
10
            //Wait two seconds
            SDL_Delay( 2000 );
        }
    }
 
    //Free resources and close SDL
    close();
 
    return 0;
}
В лучших традициях "всё правильно, но не работает" у меня не работает.
Почему? Потому что код написан в стиле c++, а компилятор думает что код написан на чистом c. Соответственно выхода два. Или код изменить под обычный си, или кодеблоксу объяснить, что надо компилировать в плюсах.
Сейчас я переделаю код, а в следующих уроках мы перейдём на c++. Кроме того, в коде будут изменения. Я попытаюсь нарисовать футбольный мячик на зеленом поле:
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <SDL.h>
#include <stdio.h>
 
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
 
int init(); // для инициализации. вместо типа bool пришлось поставить int
int loadMedia(); // для загрузки изображений
void close(); // для закрытия
 
SDL_Window* gWindow = NULL;
SDL_Surface* gScreenSurface = NULL;
SDL_Surface* gBall = NULL; // имя переделали для мяча
 
int init()
{
    int success = 1; // вместо bool пришлось использовать int и придавать числовые значения
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
        success = 0;
    }
    else
    {
        gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
        if( gWindow == NULL )
        {
            printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
            success = 0;
        }
        else
        {
            gScreenSurface = SDL_GetWindowSurface( gWindow ); // получаем поверхность окна
        }
    }
    return success;
}
 
int loadMedia()
{
    int success = 1;
    gBall = SDL_LoadBMP( "ball100.bmp" ); // размер нашего рисунка мяча 100*100 точек
    if( gBall == NULL )
    {
        printf( "Unable to load image %s! SDL Error: %s\n", "ball100.bmp", SDL_GetError() );
        success = 0;
    }
    return success;
}
 
void close()
{
    SDL_FreeSurface( gBall ); // высвобождаем поверхности
    gBall = NULL;
    SDL_DestroyWindow( gWindow );
    gWindow = NULL;
    SDL_Quit();
}
 
int main( int argc, char* args[] )
{
    if( !init() )
    {
        printf( "Failed to initialize!\n" );
    }
    else
    {
        if( !loadMedia() )
        {
            printf( "Failed to load media!\n" );
        }
        else
        {
            // заливаем поле зеленым
            SDL_FillRect( gScreenSurface, NULL, SDL_MapRGB( gScreenSurface->format, 0x00, 0x88, 0x00 ) );
            // рисуем мяч на поверхности окна
            SDL_BlitSurface( gBall, NULL, gScreenSurface, NULL );
            // обновляем окно
            SDL_UpdateWindowSurface( gWindow );
            SDL_Delay( 5000 );
        }
    }
    SDL_DestroyWindow( gWindow  );
    SDL_Quit();
    return 0;
}

Вот что получилось. Как видите, по углам остались белые уголки. В следующих уроках мы научимся использовать прозрачность для исправления этого, и отлавливать нажатия кнопок.
Нажмите на изображение для увеличения
Название: 2015-08-24 09 05 37.png
Просмотров: 546
Размер:	33.1 Кб
ID:	3276
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru