SDL2 доступ рендеринга кадрового буфера - PullRequest
0 голосов
/ 11 февраля 2020

Я пытаюсь получить доступ к кадровому буферу SDL_Renderer в виде массива пикселей Uint32 * для передачи в функцию отображения Libretro.

Я создал этот минимальный пример, чтобы показать два метода доступа к кадровому буферу SDL, которые я видел онлайн, ни одна из которых не работает.

#include <iostream>
#include "SDL.h"

const int WIDTH = 50;
const int HEIGHT = 40;
int frameIndex = 0;

int main(int argc, char *argv[]) {
    SDL_Init(SDL_INIT_VIDEO);

    auto win = SDL_CreateWindow("Framebuffer test", SDL_WINDOWPOS_CENTERED | SDL_WINDOW_OPENGL, SDL_WINDOWPOS_CENTERED, WIDTH, HEIGHT, 0);

    auto renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);

    auto pixels = new uint32_t[WIDTH * HEIGHT]; // width x height x SDL_PIXELFORMAT_RGBA8888
    auto surface = SDL_CreateRGBSurface(SDL_WINDOW_SHOWN, WIDTH, HEIGHT, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);

    // Rect to draw on the screen
    SDL_Rect rect;
    rect.x = 0;
    rect.y = 0;
    rect.w = 10;
    rect.h = 10;

    while (true) {
        SDL_Event e;
        if (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT) {
                break;
            }
        }

        SDL_RenderClear(renderer);

        SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
        SDL_RenderDrawRect(renderer, &rect);
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);

        // Access framebuffer: method 1
        SDL_RenderReadPixels(renderer, nullptr, SDL_PIXELFORMAT_ARGB8888, pixels, 1);
        auto surfacePixels = (Uint32*) surface->pixels;
        for (int i = 0; i < WIDTH * HEIGHT; ++i)
            surfacePixels[i] = pixels[i];
        SDL_LockSurface(surface);
        SDL_SaveBMP(surface, ("screen1_" + std::to_string(frameIndex) + ".bmp").c_str()); // Output buffer
        SDL_UnlockSurface(surface);

        // Access frame buffer: method 2
        auto surf = SDL_GetWindowSurface(win);
        SDL_LockSurface(surf);
        SDL_SaveBMP(surf, ("screen2_" + std::to_string(frameIndex) + ".bmp").c_str()); // Output buffer
        SDL_UnlockSurface(surf);
        std::cout << "Screenshot: " << frameIndex++ << std::endl;

        SDL_RenderPresent(renderer);

        if (frameIndex >= 20)
            break;
    }

    SDL_FreeSurface(surface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(win);

    SDL_Quit();

    return 0;
}

Окно / желаемый вывод

Window

Результат метода 1

Method 1

Результат метода 2

Method 2

Я ожидаю увидеть черный экран с белым квадрат, но в первом методе я вижу шум, а во втором методе я вижу черный экран. Что я делаю не так?

1 Ответ

2 голосов
/ 11 февраля 2020

Ваш SDL_RenderReadPixels параметр шага равен 1. Шаг - длина байта одной строки изображения. Если каждый пиксель имеет ширину 4 байта, а каждая строка равна 1 байту ... изображение имеет ширину 1/4 пикселя? Звучит проблематично c. Правильный шаг - 4 * WIDTH (плюс заполнение, но у вас его нет в 32-битных форматах).

Другие замечания:

SDL_CreateRGBSurface первый параметр - "flags - флаги не используются и должны быть установлены на 0 ", а не SDL_WINDOW_SHOWN. Ничего не меняет, так как игнорируется, но, по крайней мере, вводит в заблуждение.

SDL_GetWindowSurface нельзя использовать, если с окном связан рендерер. Документация гласит: «Вы не можете комбинировать это с 3D или API рендеринга в этом окне».

SDL_LockSurface должно быть до доступа к поверхностным пикселям, с разблокировкой после него. На большинстве поверхностей блокировка не требуется, поэтому вы не видите ничего другого, но блокировка до SaveBMP логически некорректна.

...