Простая кросс-платформенная библиотека C для работы с окнами/вводом, чтобы отображать сырые массивы пикселей.

Вопрос или проблема

Я хочу создать свою собственную библиотеку рендеринга примитивным способом, не используя OpenGL или что-то подобное. Но я не хочу связываться с управлением окнами/вводом, специфичным для платформы. Поэтому мне нужна библиотека, подобная GLFW. Но, насколько я знаю, GLFW не может рендерить сырые пиксельные данные.

</div><div class="s-prose js-post-body" itemprop="text">

Tsoding использует SDL для этого:

    SDL_Window *window = NULL;
    SDL_Renderer *renderer = NULL;

    {
        if (SDL_Init(SDL_INIT_VIDEO) < 0) return_defer(1);

        window = SDL_CreateWindow("Olivec", 0, 0, 0, 0, 0);
        if (window == NULL) return_defer(1);

        renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
        if (renderer == NULL) return_defer(1);

        Uint32 prev = SDL_GetTicks();
        bool pause = false;
        for (;;) {
            // Вычислить время между кадрами
            Uint32 curr = SDL_GetTicks();
            float dt = (curr - prev)/1000.f;
            prev = curr;

            // Обработать события
            SDL_Event event;
            while (SDL_PollEvent(&event)) {
                switch (event.type) {
                case SDL_QUIT: {
                    return_defer(0);
                } break;
                case SDL_KEYDOWN: {
                    if (event.key.keysym.sym == SDLK_SPACE) pause = !pause;
                } break;
                }
            }

            SDL_Rect window_rect = {0, 0, vc_sdl_actual_width, vc_sdl_actual_height};

            if (!pause) {
                // Отрисовать текстуру
                Olivec_Canvas oc_src = vc_render(dt);
                if (oc_src.width != vc_sdl_actual_width || oc_src.height != vc_sdl_actual_height) {
                    if (!vc_sdl_resize_texture(renderer, oc_src.width, oc_src.height)) return_defer(1);
                    SDL_SetWindowSize(window, vc_sdl_actual_width, vc_sdl_actual_height);
                }
                void *pixels_dst;
                int pitch;
                if (SDL_LockTexture(vc_sdl_texture, &window_rect, &pixels_dst, &pitch) < 0) return_defer(1);
                for (size_t y = 0; y < vc_sdl_actual_height; ++y) {
                    // TODO: было бы здорово, если бы Olivec_Canvas поддерживал шаг в байтах вместо пикселей
                    // Это было бы более гибким, и мы могли бы рисовать непосредственно в заблокированную память текстуры
                    memcpy((char*)pixels_dst + y*pitch, oc_src.pixels + y*vc_sdl_actual_width, vc_sdl_actual_width*sizeof(uint32_t));
                }
                SDL_UnlockTexture(vc_sdl_texture);
            }

            // Отобразить текстуру
            if (SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0) < 0) return_defer(1);
            if (SDL_RenderClear(renderer) < 0) return_defer(1);
            if (SDL_RenderCopy(renderer, vc_sdl_texture, &window_rect, &window_rect) < 0) return_defer(1);
            SDL_RenderPresent(renderer);
        }
    }
</div>.

Ответ или решение

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

Зачем использовать SDL?

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

Пример реализации рендеринга пикселей с использованием SDL

Ниже приведен пример кода, который демонстрирует, как с помощью SDL создать окно и рендерить массив пикселей.

#include <SDL2/SDL.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>

void render_pixels(SDL_Renderer *renderer, uint32_t *pixels, int width, int height) {
    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
    if (!texture) {
        fprintf(stderr, "Unable to create texture: %s\n", SDL_GetError());
        return;
    }

    void *texture_pixels;
    int pitch;
    if (SDL_LockTexture(texture, NULL, &texture_pixels, &pitch) < 0) {
        fprintf(stderr, "Unable to lock texture: %s\n", SDL_GetError());
        return;
    }

    memcpy(texture_pixels, pixels, height * pitch);

    SDL_UnlockTexture(texture);
    SDL_RenderCopy(renderer, texture, NULL, NULL);
    SDL_DestroyTexture(texture);
}

int main(int argc, char *argv[]) {
    SDL_Window *window = NULL;
    SDL_Renderer *renderer = NULL;

    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "Could not initialize SDL: %s\n", SDL_GetError());
        return 1;
    }

    window = SDL_CreateWindow("Pixel Renderer", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_SHOWN);
    if (!window) {
        fprintf(stderr, "Could not create window: %s\n", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (!renderer) {
        fprintf(stderr, "Could not create renderer: %s\n", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    uint32_t pixels[800 * 600];  // массив для хранения пикселей
    memset(pixels, 0, sizeof(pixels));  // инициализация черным цветом

    // Основной цикл
    bool running = true;
    while (running) {
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                running = false;
            }
        }

        render_pixels(renderer, pixels, 800, 600);

        SDL_RenderPresent(renderer);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

Объяснение кода

  • Инициализация SDL: Код начинается с инициализации библиотеки SDL.
  • Создание окна и рендерера: Мы создаем окно и рендерер, которые необходимы для отображения графики.
  • Рендеринг пикселей: В функции render_pixels мы создаем текстуру, на которую будем записывать данные о пикселях. Мы используем SDL_LockTexture для получения прямого доступа к памяти текстуры и копируем туда массив пикселей.
  • Основной цикл: В основном цикле приложения обрабатываются события (например, закрытие окна), и вызывается функция рендеринга.

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...