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

SDL3 для Desktop (MinGW): Вывод текста со шрифтом TTF с помощью библиотеки SDL3_ttf на Си и C++

Запись от 8Observer8 размещена 24.03.2026 в 06:18. Обновил(-а) 8Observer8 21.03.2026 в 02:12
Показов 2313 Комментарии 0
Метки bash, c, c++, cmake, gamedev, sdl, sdl3, си

Содержание блога

Финальные проекты на Си и на C++:


Результат:



Напишем программу с нуля, которая выводит текст на русском с помощью библиотеки SDL3_ttf и рисует отрезками равносторонний треугольник.

Установка инструментов



  • Установите CMake и MinGW 13.1 по пошаговой инструкции: Установка CMake и MinGW 13.1
  • Скачайте следующие архивы библиотек:
  • Извлеките содержимое архивов выше в какую-нибудь общую папку, например, с именем "libs" на C:
  • Примечание. Эти архивы я скачал с официальных репозиториев на вкладках Release: SDL3 3.4.2 и SDL3_ttf 3.2.2. Я сократил глубину вложенность папок в архивах со словом "devel" в имени.
  • Пути к папкам с "win32-x64" в именах нужно добавить в переменную окружения Path. В этих папках лежат SDL3.dll и SDL3_ttf.dll. Добавьте пути к этим папкам в Path:
    • C:\libs\SDL3-3.4.2-win32-x64
    • C:\libs\SDL3_ttf-3.2.2-win32-x64
  • Установите редактор кода или IDE, например: Notepad++ или VSCode. Я использую Sublime Text 4 и Qt Creator

Настройка и запуск стартового проекта



  • Скачайте стартовый проект: start-text-sdl3-c.zip
  • Примечание. Этот стартовый проект был создан по пошаговой инструкции: Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
  • Извлеките пример из архива:
    Название: 7ea1de96-8017-4103-bafb-7724a2bed04f.png
Просмотров: 627

Размер: 3.0 Кб
  • Код стартового примера:

    main.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
    
    #define SDL_MAIN_USE_CALLBACKS 1 // Использовать функции обратного вызова вместо main()
    #include <SDL3/SDL.h>
    #include <SDL3/SDL_main.h>
     
    // Мы будем использовать этот рендерер для рисования в этом окне каждый кадр
    static SDL_Window *window = NULL;
    static SDL_Renderer *renderer = NULL;
     
    // Эта функция запускается один раз при старте
    SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
    {
        if (!SDL_Init(SDL_INIT_VIDEO))
        {
            SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
            return SDL_APP_FAILURE;
        }
     
        if (!SDL_CreateWindowAndRenderer("Рендерер SDL3", 400, 300, 0, &window, &renderer))
        {
            SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
            return SDL_APP_FAILURE;
        }
     
        SDL_SetRenderVSync(renderer, 1);
     
        return SDL_APP_CONTINUE; // Продолжаем работу программы
    }
     
    // Эта функция запускается, когда происходит новое событие (ввод мыши, нажатие клавиш и т.д.)
    SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
    {
        if (event->type == SDL_EVENT_QUIT)
        {
            return SDL_APP_SUCCESS; // Завершаем программу, сообщая ОС об успехе
        }
        return SDL_APP_CONTINUE; // Продолжаем работу программы
    }
     
    // Эта функция запускается один раз за кадр и является сердцем программы
    SDL_AppResult SDL_AppIterate(void *appstate)
    {
        SDL_SetRenderDrawColor(renderer, 38, 43, 51, 255); // Цвет заливки холста
        SDL_RenderClear(renderer); // Начинаем с чистого холста - заливаем холст указанным выше цветов
     
        // Выводим результат на экран
        SDL_RenderPresent(renderer);
     
        return SDL_APP_CONTINUE; // Продолжаем работу программы
    }
     
    // Эта функция запускается один раз при завершении работы
    void SDL_AppQuit(void *appstate, SDL_AppResult result)
    {
        // SDL3 автоматически освободит все ресурсы подсистем (окна, рендереры) при выходе
    }
    CMakeLists.txt

    Bash
    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
    
    cmake_minimum_required(VERSION 3.21)
    project(start-text-sdl3-c)
     
    # Устанавливаем стандарт C
    set(CMAKE_C_STANDARD 17)
    set(CMAKE_C_STANDARD_REQUIRED ON)
     
    # Задаем название будущего приложения (в Windows это будет app.exe, а
    # в вебе было бы app.js / app.wasm)
    add_executable(app)
     
    # Указываем точное местоположение конфигурационных файлов библиотек
    # Имя переменной должно строго соответствовать: НазваниеБиблиотеки_DIR
    set(SDL3_DIR "C:/libs/SDL3-devel-3.4.2-mingw/lib/cmake/SDL3")
     
    # Добавляем исходный код к проекту
    target_sources(app
        PRIVATE
        src/main.c
    )
     
    # Проверяем наличие библиотек в системе
    # Если она не будет найдены, CMake прервет настройку с ошибкой
    # REQUIRED - переводится, как "обязательно" или "требуется"
    find_package(SDL3 REQUIRED)
     
    # Привязываем библиотеки к нашему приложению
    target_link_libraries(app PRIVATE SDL3::SDL3)
    config-exe.bat

    Bash
    1
    2
    
    cmake -G "MinGW Makefiles" -S . -B dist/win ^
    -DCMAKE_BUILD_TYPE=Debug
    build-exe.bat

    Bash
    1
    2
    3
    
    cd dist\win
    cmake --build .
    cd ..\..
    run-exe.bat

    Bash
    1
    
    dist\win\app
  • Откройте папку примера в каком-нибудь текстовом редакторе - например, в Sublime Text 4 (ST4), Notepad++, VS Code и т.д. В ST4 можно открыть проекта, набрав в консоли из папки проекта: "subl ." (без ковычек). Либо откройте проект в Qt Creator - для этого в левом верхнем углу Qt Creator нажмите "Fire" > "Open File or Project..." и выберите файл "CMakeLists.txt" из папки скаченного проекта
  • Откройте файл "CMakeLists.txt" и убедитесь, что путь к библиотеке SDL3 такой же как у вас и при необходимости измените его на ваш путь:
    Bash
    1
    
    set(SDL3_DIR "C:/libs/SDL3-devel-3.4.2-mingw/lib/cmake/SDL3")
  • Откройте консоль в текущей папке и поочерёдно введите команды для конфигурирования, сборки и запуска EXE:
    Bash
    1
    2
    3
    
    config-exe
    build-exe
    run-exe
  • Будет запущено приложение, которое создаёт окно с помощью библиотеки SDL3 и заливает холст заданным цветом с помощью SDL3 Renderer:
  • Мы настроили и протестировали корректный запуск стартового примера

Подключение библиотеки SDL3_ttf



  • Откройте файл "CMakeLists.txt" в редакторе кода
  • Добавьте создание переменной SDL3_ttf_DIR с значением пути, куда вы распаковали архив "SDL3_ttf-devel-3.2.2-mingw":
    Bash
    1
    
    set(SDL3_ttf_DIR "C:/libs/SDL3_ttf-devel-3.2.2-mingw/lib/cmake/SDL3_ttf")
  • Добавьте команду find_package для SDL3_ttf:
    Bash
    1
    2
    3
    
    # Загружаем настройки пакетов (параметры компиляции и пути к заголовкам)
    find_package(SDL3 REQUIRED)
    find_package(SDL3_ttf REQUIRED)
  • Добавьте привязку библиотеки SDL3_ttf к своему приложению:
    Bash
    1
    2
    
    # Привязываем библиотеки к нашему приложению
    target_link_libraries(app PRIVATE SDL3_ttf::SDL3_ttf SDL3::SDL3)
  • В результате получается следующий файл "CMakeLists.txt":

    CMakeLists.txt

    Bash
    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
    
    cmake_minimum_required(VERSION 3.21)
    project(finish-text-sdl3-c)
     
    # Устанавливаем стандарт C
    set(CMAKE_C_STANDARD 17)
    set(CMAKE_C_STANDARD_REQUIRED ON)
     
    # Задаем название будущего приложения (в Windows это будет app.exe, а
    # в вебе было бы app.js / app.wasm)
    add_executable(app)
     
    # Указываем точное местоположение конфигурационных файлов библиотек
    # Имя переменной должно строго соответствовать: НазваниеБиблиотеки_DIR
    set(SDL3_DIR "C:/libs/SDL3-devel-3.4.2-mingw/lib/cmake/SDL3")
    set(SDL3_ttf_DIR "C:/libs/SDL3_ttf-devel-3.2.2-mingw/lib/cmake/SDL3_ttf")
     
    # Добавляем исходный код к проекту
    target_sources(app
        PRIVATE
        src/main.c
    )
     
    # Проверяем наличие библиотек в системе
    # Если она не будет найдены, CMake прервет настройку с ошибкой
    # REQUIRED - переводится, как "обязательно" или "требуется"
    find_package(SDL3 REQUIRED)
    find_package(SDL3_ttf REQUIRED)
     
    # Привязываем библиотеки к нашему приложению
    target_link_libraries(app PRIVATE SDL3_ttf::SDL3_ttf SDL3::SDL3)

Загрузка TTF-файла и вывод текста на холст



  • Скачайте бесплатный шрифт: LiberationSans-Regular.zip
  • Примечание. Шрифт был взять по ссылке
  • Извлеките архив с файлом шрифта в новую папку "assets/fonts" внутри проекта
  • Откройте в редакторе кода файл "src/main.c"
  • Подключите заголовок "SDL_ttf.h" в начале файла:
    C
    1
    2
    3
    4
    
    #define SDL_MAIN_USE_CALLBACKS 1 // Использовать функции обратного вызова вместо main()
    #include <SDL3/SDL.h>
    #include <SDL3/SDL_main.h>
    #include <SDL3_ttf/SDL_ttf.h>
  • После создания переменной-указателя "renderer" добавьте переменную-указатель "textTexture" для текстуры, на которую будет рисоваться текст
  • Сразу, чтобы не забыть освободить ресурс, добавьте удаление текстуры в функцию SDL_AppQuit() и добавьте завершить работу SDL3_ttf:
    C
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    // Эта функция запускается один раз при завершении работы
    void SDL_AppQuit(void *appstate, SDL_AppResult result)
    {
        if (textTexture)
        {
            SDL_DestroyTexture(textTexture);
        }
     
        TTF_Quit();
     
        // SDL3 автоматически освободит все ресурсы подсистем (окна, рендереры) при выходе
    }
  • В функции SDL_AppInit() после вызова SDL_Init() добавьте инициализацию библиотеки SDL3_ttf:
    C
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    // Эта функция запускается один раз при старте
    SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
    {
        if (!SDL_Init(SDL_INIT_VIDEO))
        {
            SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
            return SDL_APP_FAILURE;
        }
     
        if (!TTF_Init())
        {
            SDL_Log("Couldn't initialize TTF: %s", SDL_GetError());
            return SDL_APP_FAILURE;
        }
  • После вызова функции включения вертикальной синхронизации SDL_SetRenderVSync() добавьте код загрузки файла-шрифта и создания текстуры для текста:
    C
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
        SDL_SetRenderVSync(renderer, 1);
     
        TTF_Font *font = TTF_OpenFont("assets/fonts/LiberationSans-Regular.ttf", 28);
        if (!font)
        {
            SDL_Log("TTF_OpenFont failed: %s", SDL_GetError());
            return SDL_APP_FAILURE;
        }
        SDL_Color textColor = (SDL_Color) { 245, 240, 220 };
        SDL_Surface *surface = TTF_RenderText_Blended(font, "Равносторонний треугольник", 0, textColor);
        texture = SDL_CreateTextureFromSurface(renderer, surface);
        SDL_DestroySurface(surface);
        TTF_CloseFont(font);
  • В цикле рисования, то есть в функции SDL_AppIterate(), добавьте код рисования текста:
    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
    
    // Эта функция запускается один раз за кадр и является сердцем программы
    SDL_AppResult SDL_AppIterate(void *appstate)
    {
        // Устанавливаем позицию, в которой хотим нарисовать текст
        float x = 10.f;
        float y = 30.f;
     
        // Создаем прямоугольник для хранения размеров текстуры
        SDL_FRect rect;
        SDL_GetTextureSize(textTexture, &rect.w, &rect.h);
        rect.x = x;
        rect.y = y;
     
        // Очищаем экран
        SDL_SetRenderDrawColor(renderer, 38, 43, 51, 255); // Цвет заливки холста
        // Начинаем с чистого холста - заливаем холст указанным выше цветов
        SDL_RenderClear(renderer);
     
        // Отрисовываем текст
        SDL_RenderTexture(renderer, textTexture, NULL, &rect);
     
        // Выводим результат на экран
        SDL_RenderPresent(renderer);
     
        return SDL_APP_CONTINUE; // Продолжаем работу программы
    }
  • Запустите сборку командой "build-exe" и запустите приложение командой "run-exe". Текст выводится на экран:
  • Текущий код для вывода текста:

    main.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
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    
    #define SDL_MAIN_USE_CALLBACKS 1 // Использовать функции обратного вызова вместо main()
    #include <SDL3/SDL.h>
    #include <SDL3/SDL_main.h>
    #include <SDL3_ttf/SDL_ttf.h>
     
    // Мы будем использовать этот рендерер для рисования в этом окне каждый кадр
    static SDL_Window *window = NULL;
    static SDL_Renderer *renderer = NULL;
    static SDL_Texture *textTexture = NULL;
     
    // Эта функция запускается один раз при старте
    SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
    {
        if (!SDL_Init(SDL_INIT_VIDEO))
        {
            SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
            return SDL_APP_FAILURE;
        }
     
        if (!TTF_Init())
        {
            SDL_Log("Couldn't initialize TTF: %s", SDL_GetError());
            return SDL_APP_FAILURE;
        }
     
        if (!SDL_CreateWindowAndRenderer("Рендерер SDL3", 400, 300, 0, &window, &renderer))
        {
            SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
            return SDL_APP_FAILURE;
        }
     
        SDL_SetRenderVSync(renderer, 1);
     
        TTF_Font *font = TTF_OpenFont("assets/fonts/LiberationSans-Regular.ttf", 28);
        if (!font)
        {
            SDL_Log("TTF_OpenFont failed: %s", SDL_GetError());
            return SDL_APP_FAILURE;
        }
        SDL_Color textColor = (SDL_Color) { 245, 240, 220 };
        SDL_Surface *surface = TTF_RenderText_Blended(font, "Равносторонний треугольник", 0, textColor);
        textTexture = SDL_CreateTextureFromSurface(renderer, surface);
        SDL_DestroySurface(surface);
     
        return SDL_APP_CONTINUE; // Продолжаем работу программы
    }
     
    // Эта функция запускается, когда происходит новое событие (ввод мыши, нажатие клавиш и т.д.)
    SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
    {
        if (event->type == SDL_EVENT_QUIT)
        {
            return SDL_APP_SUCCESS; // Завершаем программу, сообщая ОС об успехе
        }
        return SDL_APP_CONTINUE; // Продолжаем работу программы
    }
     
    // Эта функция запускается один раз за кадр и является сердцем программы
    SDL_AppResult SDL_AppIterate(void *appstate)
    {
        // Устанавливаем позицию, в которой хотим нарисовать текст
        float x = 10.f;
        float y = 30.f;
     
        // Создаем прямоугольник для хранения размеров текстуры
        SDL_FRect rect;
        SDL_GetTextureSize(textTexture, &rect.w, &rect.h);
        rect.x = x;
        rect.y = y;
     
        // Очищаем экран
        SDL_SetRenderDrawColor(renderer, 38, 43, 51, 255); // Цвет заливки холста
        // Начинаем с чистого холста - заливаем холст указанным выше цветов
        SDL_RenderClear(renderer);
     
        // Отрисовываем текст
        SDL_RenderTexture(renderer, textTexture, NULL, &rect);
     
        // Выводим результат на экран
        SDL_RenderPresent(renderer);
     
        return SDL_APP_CONTINUE; // Продолжаем работу программы
    }
     
    // Эта функция запускается один раз при завершении работы
    void SDL_AppQuit(void *appstate, SDL_AppResult result)
    {
        if (textTexture)
        {
            SDL_DestroyTexture(textTexture);
            textTexture = NULL;
        }
     
        TTF_Quit();
     
        // SDL3 автоматически освободит все ресурсы подсистем (окна, рендереры) при выходе
    }

Далее, два бонусных примера, которые сгенерировал ИИ Gemini.

Рисуем координатные оси и правильный треугольник





main.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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#define SDL_MAIN_USE_CALLBACKS 1 // Использовать функции обратного вызова вместо main()
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3_ttf/SDL_ttf.h>
 
// Мы будем использовать этот рендерер для рисования в этом окне каждый кадр
static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;
static SDL_Texture *textTexture = NULL;
static TTF_Font *font = NULL;
 
// Текстуры для подписей вершин
static SDL_Texture *coordTextures[3] = { NULL, NULL, NULL };
 
// Вспомогательная функция для создания текстуры из строки
SDL_Texture* CreateTextTexture(const char* text, SDL_Color color)
{
    SDL_Surface *surface = TTF_RenderText_Blended(font, text, 0, color);
    SDL_Texture *tex = SDL_CreateTextureFromSurface(renderer, surface);
    SDL_DestroySurface(surface);
    return tex;
}
 
// Эта функция запускается один раз при старте
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
    if (!SDL_Init(SDL_INIT_VIDEO))
    {
        SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    if (!TTF_Init())
    {
        SDL_Log("Couldn't initialize TTF: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    if (!SDL_CreateWindowAndRenderer("Рендерер SDL3", 400, 300, 0, &window, &renderer))
    {
        SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    SDL_SetRenderVSync(renderer, 1);
 
    TTF_Font *font = TTF_OpenFont("assets/fonts/LiberationSans-Regular.ttf", 28);
    if (!font)
    {
        SDL_Log("TTF_OpenFont failed: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
    SDL_Color textColor = (SDL_Color) { 245, 240, 220 };
    SDL_Surface *surface = TTF_RenderText_Blended(font, "Равносторонний треугольник", 0, textColor);
    textTexture = SDL_CreateTextureFromSurface(renderer, surface);
    SDL_DestroySurface(surface);
    TTF_CloseFont(font);
 
    return SDL_APP_CONTINUE; // Продолжаем работу программы
}
 
// Эта функция запускается, когда происходит новое событие (ввод мыши, нажатие клавиш и т.д.)
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
    if (event->type == SDL_EVENT_QUIT)
    {
        return SDL_APP_SUCCESS; // Завершаем программу, сообщая ОС об успехе
    }
    return SDL_APP_CONTINUE; // Продолжаем работу программы
}
 
// Эта функция запускается один раз за кадр и является сердцем программы
SDL_AppResult SDL_AppIterate(void *appstate)
{
    int winW, winH;
    SDL_GetWindowSize(window, &winW, &winH);
 
    // Очистка экрана
    SDL_SetRenderDrawColor(renderer, 38, 43, 51, 255);
    SDL_RenderClear(renderer);
 
    // Отрисовка текста (в обычных координатах: Y вниз)
    float tw, th;
    SDL_GetTextureSize(textTexture, &tw, &th);
    SDL_FRect textRect = { (winW - tw) / 2.0f, 20.0f, tw, th };
    SDL_RenderTexture(renderer, textTexture, NULL, &textRect);
 
    // Центр координат: середина по X, и чуть ниже текста по Y
    float originX = winW / 2.0f;
    float originY = winH / 2.0f + 40.0f;
 
    // Вспомогательная функция для перевода наших координат в экранные
    // Экранное_X = originX + наш_X
    // Экранное_Y = originY - наш_Y (минус, так как в SDL Y растет вниз, а нам нужно вверх)
 
    // Рисуем оси
    // Ось X
    SDL_SetRenderDrawColor(renderer, 180, 80, 80, 255);
    SDL_RenderLine(renderer, originX - 150, originY, originX + 150, originY);
    // Ось Y
    SDL_SetRenderDrawColor(renderer, 80, 180, 80, 255);
    SDL_RenderLine(renderer, originX, originY + 100, originX, originY - 120);
 
    // Рисуем правильный треугольник
    float a = 150.0f; // Сторона треугольника
    float h = a * SDL_sqrtf(3.0f) / 2.0f;
    float r_in = h / 3.0f;         // Расстояние от центра до основания
    float r_out = 2.0f * h / 3.0f; // Расстояние от центра до вершины
 
    // Координаты вершин относительно центра (0,0), где Y смотрит вверх
    SDL_FPoint trianglePoints[4] = {
        { originX - a / 2.0f, originY + r_in }, // Лево-низ (в SDL это +r_in, т.к. экранный Y вниз)
        { originX + a / 2.0f, originY + r_in }, // Право-низ
        { originX, originY - r_out },           // Вершина (в SDL это -r_out, чтобы была вверху)
        { originX - a / 2.0f, originY + r_in }  // Замыкаем
    };
 
    SDL_SetRenderDrawColor(renderer, 245, 240, 220, 255); // Светло-кремовый
    SDL_RenderLines(renderer, trianglePoints, 4);
 
    SDL_RenderPresent(renderer);
    return SDL_APP_CONTINUE;
}
 
// Эта функция запускается один раз при завершении работы
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
    if (textTexture)
    {
        SDL_DestroyTexture(textTexture);
    }
 
    TTF_Quit();
 
    // SDL3 автоматически освободит все ресурсы подсистем (окна, рендереры) при выходе
}

Подписи координат вершин треугольника





main.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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <stdio.h>
 
static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;
static SDL_Texture *textTexture = NULL;
static TTF_Font *font = NULL;
static SDL_Texture *coordTextures[3] = { NULL, NULL, NULL };
 
static const float TRIANGLE_A = 280.0f; // Длина стороны треугольника
static const float AXIS_X_LEN = 200.0f; // Половина длины оси X
static const float AXIS_Y_LEN = 180.0f; // Длина осей вверх/вниз
 
SDL_Texture *CreateTextTexture(const char *text, SDL_Color color)
{
    SDL_Surface *surface = TTF_RenderText_Blended(font, text, 0, color);
    SDL_Texture *tex = SDL_CreateTextureFromSurface(renderer, surface);
    SDL_DestroySurface(surface);
    return tex;
}
 
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
    if (!SDL_Init(SDL_INIT_VIDEO))
        return SDL_APP_FAILURE;
    if (!TTF_Init())
        return SDL_APP_FAILURE;
 
    // Увеличили окно для большого треугольника
    if (!SDL_CreateWindowAndRenderer("Рендерер SDL3", 500, 500, 0, &window, &renderer))
        return SDL_APP_FAILURE;
 
    SDL_SetRenderVSync(renderer, 1);
 
    // Загружаем шрифт с базовым размером
    font = TTF_OpenFont("assets/fonts/LiberationSans-Regular.ttf", 20);
    if (!font)
        return SDL_APP_FAILURE;
 
    SDL_Color titleColor = { 245, 240, 220, 255 };
    SDL_Color coordColor = { 200, 200, 200, 255 };
 
    // Создаем заголовок
    TTF_SetFontSize(font, 28);
    textTexture = CreateTextTexture("Равносторонний треугольник", titleColor);
 
    // Рассчитываем и создаем подписи координат
    TTF_SetFontSize(font, 20); // Размер для координат
    float h = TRIANGLE_A * SDL_sqrtf(3.0f) / 2.0f;
    float r_in = h / 3.0f;
    float r_out = 2.0f * h / 3.0f;
 
    char buf[64];
    // Лево-низ
    sprintf(buf, "(%.1f, %.1f)", -TRIANGLE_A / 2.0f, -r_in);
    coordTextures[0] = CreateTextTexture(buf, coordColor);
    // Право-низ
    sprintf(buf, "(%.1f, %.1f)", TRIANGLE_A / 2.0f, -r_in);
    coordTextures[1] = CreateTextTexture(buf, coordColor);
    // Верх
    sprintf(buf, "(0.0, %.1f)", r_out);
    coordTextures[2] = CreateTextTexture(buf, coordColor);
 
    return SDL_APP_CONTINUE;
}
 
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
    if (event->type == SDL_EVENT_QUIT)
        return SDL_APP_SUCCESS;
    return SDL_APP_CONTINUE;
}
 
SDL_AppResult SDL_AppIterate(void *appstate)
{
    int winW, winH;
    SDL_GetWindowSize(window, &winW, &winH);
 
    SDL_SetRenderDrawColor(renderer, 38, 43, 51, 255);
    SDL_RenderClear(renderer);
 
    // Отрисовка заголовка
    float tw, th;
    SDL_GetTextureSize(textTexture, &tw, &th);
    SDL_FRect textRect = { (winW - tw) / 2.0f, 20.0f, tw, th };
    SDL_RenderTexture(renderer, textTexture, NULL, &textRect);
 
    // Оси координат
    float originX = winW / 2.0f;
    float originY = winH / 2.0f + 50.0f; // Центр системы координат
 
    SDL_SetRenderDrawColor(renderer, 180, 80, 80, 255); // Мягкий красный
    SDL_RenderLine(renderer, originX - AXIS_X_LEN, originY, originX + AXIS_X_LEN, originY);
 
    SDL_SetRenderDrawColor(renderer, 80, 180, 80, 255); // Мягкий зеленый
    SDL_RenderLine(renderer, originX, originY + AXIS_Y_LEN, originX, originY - AXIS_Y_LEN);
 
    // Расчет точек треугольника
    float h = TRIANGLE_A * SDL_sqrtf(3.0f) / 2.0f;
    float r_in = h / 3.0f;
    float r_out = 2.0f * h / 3.0f;
 
    SDL_FPoint pts[4] = {
        { originX - TRIANGLE_A / 2.0f, originY + r_in },
        { originX + TRIANGLE_A / 2.0f, originY + r_in },
        { originX, originY - r_out },
        { originX - TRIANGLE_A / 2.0f, originY + r_in }
    };
 
    SDL_SetRenderDrawColor(renderer, 245, 240, 220, 255);
    SDL_RenderLines(renderer, pts, 4);
 
    // Отрисовка подписей
    for (int i = 0; i < 3; i++)
    {
        float cw, ch;
        SDL_GetTextureSize(coordTextures[i], &cw, &ch);
        // Увеличили отступ (offset), чтобы крупный текст не налипал на вершины
        float offset = 15.0f;
        SDL_FRect cRect = {
            pts[i].x - cw / 2.0f,
            pts[i].y + (i == 2 ? -ch - offset : offset),
            cw, ch
        };
        SDL_RenderTexture(renderer, coordTextures[i], NULL, &cRect);
    }
 
    SDL_RenderPresent(renderer);
    return SDL_APP_CONTINUE;
}
 
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
    SDL_DestroyTexture(textTexture);
    for (int i = 0; i < 3; i++)
        SDL_DestroyTexture(coordTextures[i]);
    TTF_CloseFont(font);
    TTF_Quit();
}
CMakeLists.txt

Bash
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
cmake_minimum_required(VERSION 3.21)
project(finish-zoom-text-sdl3-c)
 
# Задаем название будущего приложения (в Windows это будет app.exe, а
# в вебе было бы app.js / app.wasm)
add_executable(app)
 
# Устанавливаем стандарт C
set(CMAKE_C_STANDARD 17)
set(CMAKE_C_STANDARD_REQUIRED ON)
 
# Указываем точное местоположение конфигурационных файлов библиотек
# Имя переменной должно строго соответствовать: НазваниеБиблиотеки_DIR
set(SDL3_DIR "C:/libs/SDL3-devel-3.4.2-mingw/lib/cmake/SDL3")
set(SDL3_ttf_DIR "C:/libs/SDL3_ttf-devel-3.2.2-mingw/lib/cmake/SDL3_ttf")
 
# Добавляем исходный код к проекту
target_sources(app
    PRIVATE
    src/main.c
)
 
# Проверяем наличие библиотек в системе
# Если она не будет найдены, CMake прервет настройку с ошибкой
# REQUIRED - переводится, как "обязательно" или "требуется"
find_package(SDL3 REQUIRED)
find_package(SDL3_ttf REQUIRED)
 
# Привязываем библиотеки к нашему приложению
target_link_libraries(app PRIVATE SDL3_ttf::SDL3_ttf SDL3::SDL3)
config-exe.bat

Bash
1
2
cmake -G "MinGW Makefiles" -S . -B dist/win ^
-DCMAKE_BUILD_TYPE=Debug
build-exe.bat

Bash
1
2
3
cd dist\win
cmake --build .
cd ..\..
run-exe.bat

Bash
1
dist\win\app
Миниатюры

Нажмите на изображение для увеличения
Название: a565eb29-8960-4c0c-a0ff-0ec0b5a204e3.png
Просмотров: 697
Размер:	6.4 Кб
ID:	11787
Нажмите на изображение для увеличения
Название: f3e96ab3-fc40-4975-ace3-e756d6e4dbb4.png
Просмотров: 695
Размер:	3.6 Кб
ID:	11792
Нажмите на изображение для увеличения
Название: 2209237c-1029-4462-a4a3-5f2ff14115b8.png
Просмотров: 684
Размер:	7.3 Кб
ID:	11795
Нажмите на изображение для увеличения
Название: b17e11af-3200-4ff8-bbbb-854d899aef29.png
Просмотров: 671
Размер:	9.2 Кб
ID:	11799
Нажмите на изображение для увеличения
Название: a9f8e10b-76f9-4e27-b5fd-a01418a0883a.png
Просмотров: 9504
Размер:	18.0 Кб
ID:	11801
Вложения
Тип файла: zip SDL3-devel-3.4.2-mingw.zip (2.65 Мб, 92 просмотров)
Тип файла: zip SDL3-3.4.2-win32-x64.zip (1.10 Мб, 74 просмотров)
Тип файла: zip SDL3_ttf-devel-3.2.2-mingw.zip (2.63 Мб, 93 просмотров)
Тип файла: zip SDL3_ttf-3.2.2-win32-x64.zip (979.6 Кб, 91 просмотров)
Тип файла: zip LiberationSans-Regular.zip (206.0 Кб, 53 просмотров)
Тип файла: zip start-text-sdl3-c.zip (3.0 Кб, 50 просмотров)
Тип файла: zip finish-text-sdl3-c.zip (210.0 Кб, 268 просмотров)
Тип файла: zip finish-text-sdl3-cpp.zip (210.1 Кб, 257 просмотров)
Метки bash, c, c++, cmake, gamedev, sdl, sdl3, си
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Новые блоги и статьи
Установка 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С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
Как ИИ начал спорить и врать (возможно почуяв опасность для себя от индустрии - уход от электроники).
Hrethgir 04.08.2026
Недельный диалог, на фоне событий с НПЗ. Да, из спирта можно получать бензин, и это не сложно. Но потом в схеме я решил избавиться от насоса, при этом полностью сделав контроль подачи спирта в. . .
Термопринтер QR701
Argus19 03.08.2026
Термопринтер QR701 Купил два термопринтера QR701. На сэлф-тесте написано: Language: PC936 (GB18030). Что означает, что принтеры могут печатать только латиницу и китайские иероглифы. Так же. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru