Как стать автором
Обновить

Уроки по SDL 2: Урок 4 — Растяжка PNG

Время на прочтение5 мин
Количество просмотров14K
Всем привет! Это четвертый урок по SDL 2. Я решил объеденить два урока в один, так как в оригинале они маленькие. Но их можно найти тут и тут. Что ж, начнем урок.

Сначала определимся с тем, что мы будем делать. Сегодня научимся загружать изображения другого расширения (не BMP), а именно: PNG. Переделаем изображение в другой формат для ускорения работы и изменим размеры изображения. Работать будем с этим избражением:

image

Установка


Итак, сначала научимся загружать PNG. В стандартном наборе функций SDL 2 нет загрузки изображения формата не BMP. Чтобы загрузить другой формат нужно установить расширение.

Вот инструкции:

Windows
Вы переходите на эту страницу и скачиваете архивы ZIP или TAR.GZ из раздела Development Libraries. Распаковываете и кидаете их в папку с компилятором.

Linux
Если в Вашей системе использется Debian, то вам нужно ввести эти команды:

apt-cache search libsdl2-image
apt-get install libsdl2-image-dev

Если же использется Yellowdog Updater, Modified, то вы вводите эте команды:

yum search SDL2_image-devel
yum установить SDL2_image-devel


Mac OS
Скачиваете DMG файл и читаете Readme.

Пишем код


После установки SDL_image приступаем к написанию кода. Сначала как всегда подключим библиотеки, зададим размеры окна и создадим 3 глобальных переменных.

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

SDL_Window *win = NULL;
SDL_Surface *scr = NULL;
SDL_Surface *flower = NULL;

Заметьте, что SDL_image.h я подключил отдельно, так как это отдельная библиотека.

Далее пишем 3 функции, известне нам, в которые кое-что добавим и изменим, как обычно.

Init()
bool init() {
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        std::cout << "Can't init video: " << SDL_GetError() << std::endl;
        return false;
    }

    int flags = IMG_INIT_PNG;
    if ( !( IMG_Init( flags ) & flags ) ) {
        std::cout << "Can't init image: " << IMG_GetError() << std::endl;
        return false;
    }

    win = SDL_CreateWindow("Растяжка PNG", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
    if (win == NULL) {
        std::cout << "Can't create window: " << SDL_GetError() << std::endl;
        return false;
    }

    scr = SDL_GetWindowSurface(win);

    return true;
}


Здесь мы добавили иницализацию модуля SDL_image. Чтобы его инициализировать, дляначала создаем переменную типа int, у нас это flags, и сохраняем туда все флаги, которые вы хотите использовать для инициализации.

Список всех флагов
SDL_INIT_PNG — иницализирует загрузчик PNG изображений;
SDL_INIT_JPG — иницализирует загрузчик JPG изображений;
SDL_INIT_TIF — иницализирует загрузчик TIF изображений;

Далее пишем вот такое условие:

if ( !( IMG_Init( flags ) & flags ) )

Функция IMG_Init() возвращает те флаги, которые она смогла инициализировать. Если она вернула флаги, но тот, который мы хотели отсутсвует, мы возвращаем ошибку. Очень просто. Так же, обратите внимание на то, что я использую IMG_GetError вместо SDL_GetError, так как ошибку мы ищем в функции модуля SDL_image, а не SDL.

Так же хочу отметить, что я решил использовать тип bool для returnа — это более рациональный способ.

Продолжим написание нашей программы, напишем функцию Load(), в которой загрузим PNG изображение и конвертируем его.

Load
bool load() {
    flower = IMG_Load("flower.png");
    if (flower == NULL) {
        std::cout << "Can't load: " << IMG_GetError() << std::endl;
        return false;
    }

    flower = SDL_ConvertSurface(flower, scr->format, NULL);
    if (flower == NULL) {
        std::cout << "Can't convert: " << SDL_GetError() << std::endl;
        return false;
    }

    return true;
}


Здесь мы загружаем изображение новой функцией IMG_Load. Функция у нее та же, что и у SDL_LoadBMP и результат такой же — она возвращает экземпляр класса SDL_Surface. Для поиска ошибок при загрузке так же используем IMG_GetError.

Пришло время конвертации. Сначала немного теории. Мало, кто знает, но при загрузке изображения, оно загружается в формате 24бит, а большинство современных дисплеев имеют формат 32бит. И каждый раз, когда мы отображали изображение на экране, оно сначала переделовалось в формат 32бит. Для простых программ это не имеет значения, но при создании какого-то большого проэкта, это очень сильно скажется на производительности, так что мы возьмем и на этапе загрузки изображений переделаем их в формат дисплея. В этом на поможет функция SDL_ConvertSurface. Она принимает 3 значения: Поверхность, которую мы хотим форматировать, фомат, в который мы хотим форматировать и флаги. мы хоти форматировать изображение flower, передаем его первым параметром. Вторым параметром берем формат оконной поверхности, флаги не будем использовать. Возвращает эта функция копию поверхности, которую мы сразу присвоим flower. Для поиска ошибок берем функцию SDL_GetError(), т. к. мы работаем уже с поверхностями, а они из SDL, а не из SDL_image.

Следом пишем функцию Quit, в нее добави всего одну функцию.

Quit
void quit() {
    SDL_FreeSurface(flower);
    
    SDL_FreeSurface(scr);
    SDL_DestroyWindow(win);

    SDL_Quit();
    IMG_Quit();
}


Мы добавили IMG_Quit, чтобы разынициализировать модуль SDL_image.

Main


Теперь осталось самое простое — функция main.

int main (int argc, char ** args) {
    if (!init()) {
        system("pause");
        quit();
        return 1;
    }

    if (!load()) {
        system("pause");
        quit();
        return 1;
    }

Для начала инициализируем и создадим всё, что нужно и загрузим нужные файлы. Здесь останавливаться не будем.

    SDL_Rect bg_flower;
    bg_flower.w = SCREEN_WIDTH;
    bg_flower.h = SCREEN_HEIGHT;
    bg_flower.x = 0;
    bg_flower.y = 0;

После этого создадим объект класса SDL_Rect. Он нам нужен будет для растягивания изображения. Если вы заметили, то изображение цветочка в 4 раза меньше размеров окна (в 2 раза тоньше и в 2 раза ниже), а нам надо его растянуть до размеров окна. Так что в ширину прямоугольника bg_flower записываем значение ширины окна, а в высоту — высоту окна. коорднаты ставим 0, для того, чтобы изображение отображалось в левом верхнем углу.

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

   SDL_BlitScaled(flower, NULL, scr, &bg_flower);

Она принимает 4 значения. Первое — изображение, которое мы хотим отобразить, второе — Прямоугольник, который мы хотим вырезать из этого изображения(если хотим взять всё изображение, то пишем NULL), третье — поверхность, на которую мыхотим отобразить изображение, а четвертое — тот самый прямоугольник, размеры и координаты которого мы берем для растягивания или сжатия и отображения изображения.

Дальше просто обновляем поверхность экрана, ставим задержку, выходим и возвращаем 0.

    SDL_UpdateWindowSurface(win);

    SDL_Delay(2000);

    quit();

    return 0;
};

На этом наша программа кончается. Для того, чтобы скомпилировать сее чудо, нужно добавить -lSDL2_image, как еще один параметр компиляции.

А на этом мы на сегодня закончим. Вот такой код у нас вышел:

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

SDL_Window *win = NULL;
SDL_Surface *scr = NULL;
SDL_Surface *flower = NULL;

bool init() {
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        std::cout << "Can't init video: " << SDL_GetError() << std::endl;
        return false;
    }

    int flags = IMG_INIT_PNG;
    if ( !( IMG_Init( flags ) & flags ) ) {
        std::cout << "Can't init image: " << IMG_GetError() << std::endl;
        return false;
    }

    win = SDL_CreateWindow("Растяжка PNG", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
    if (win == NULL) {
        std::cout << "Can't create window: " << SDL_GetError() << std::endl;
        return false;
    }

    scr = SDL_GetWindowSurface(win);

    return true;
}

bool load() {
    flower = IMG_Load("flower.png");
    if (flower == NULL) {
        std::cout << "Can't load: " << IMG_GetError() << std::endl;
        return false;
    }

    flower = SDL_ConvertSurface(flower, scr->format, NULL);
    if (flower == NULL) {
        std::cout << "Can't convert: " << SDL_GetError() << std::endl;
        return false;
    }

    return true;
}

void quit() {
    SDL_FreeSurface(flower);
    
    SDL_FreeSurface(scr);
    SDL_DestroyWindow(win);

    SDL_Quit();
    IMG_Quit();
}

int main (int argc, char ** args) {
    if (!init()) {
        system("pause");
        quit();
        return 1;
    }

    if (!load()) {
        system("pause");
        quit();
        return 1;
    }

    SDL_Rect bg_flower;
    bg_flower.w = SCREEN_WIDTH;
    bg_flower.h = SCREEN_HEIGHT;
    bg_flower.x = 0;
    bg_flower.y = 0;

    SDL_BlitScaled(flower, NULL, scr, &bg_flower);

    SDL_UpdateWindowSurface(win);

    SDL_Delay(2000);

    quit();

    return 0;
};

Всем пока!

<< Предыдущий урок || Следующий урок >>
Теги:
Хабы:
Всего голосов 10: ↑9 и ↓1+8
Комментарии1

Публикации

Истории

Работа

QT разработчик
4 вакансии
Программист C++
119 вакансий

Ближайшие события

28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
2 – 18 декабря
Yandex DataLens Festival 2024
МоскваОнлайн
11 – 13 декабря
Международная конференция по AI/ML «AI Journey»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань