SDL2 访问渲染帧缓冲区
SDL2 access render framebuffer
我正在尝试访问 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 / 期望输出
方法一结果
方法二结果
我希望看到带有白色方块的黑屏,但在第一种方法中我看到了噪点,而在第二种方法中我看到了黑屏。我做错了什么?
您的SDL_RenderReadPixels间距参数为1。间距是单个图像行的字节长度。如果每个像素是 4 个字节宽,每行是 1 个字节...图像是 1/4 像素宽?听起来有问题。正确的间距是 4 * WIDTH
(加上填充,但 32 位格式没有)。
其他备注:
SDL_CreateRGBSurface第一个参数是"flags - the flags are unused and should be set to 0",不是SDL_WINDOW_SHOWN
。没有任何改变,因为它被忽略了,但它至少具有误导性。
如果存在与 window 关联的渲染器,则无法使用 SDL_GetWindowSurface。文档说 "You may not combine this with 3D or the rendering API on this window".
SDL_LockSurface 应该在访问表面像素之前,在它之后解锁。在大多数表面上不需要锁定,因此您看不到任何不同,但在 SaveBMP 之前锁定在逻辑上是不正确的。
我正在尝试访问 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 / 期望输出
方法一结果
方法二结果
我希望看到带有白色方块的黑屏,但在第一种方法中我看到了噪点,而在第二种方法中我看到了黑屏。我做错了什么?
您的SDL_RenderReadPixels间距参数为1。间距是单个图像行的字节长度。如果每个像素是 4 个字节宽,每行是 1 个字节...图像是 1/4 像素宽?听起来有问题。正确的间距是 4 * WIDTH
(加上填充,但 32 位格式没有)。
其他备注:
SDL_CreateRGBSurface第一个参数是"flags - the flags are unused and should be set to 0",不是SDL_WINDOW_SHOWN
。没有任何改变,因为它被忽略了,但它至少具有误导性。
SDL_GetWindowSurface。文档说 "You may not combine this with 3D or the rendering API on this window".
SDL_LockSurface 应该在访问表面像素之前,在它之后解锁。在大多数表面上不需要锁定,因此您看不到任何不同,但在 SaveBMP 之前锁定在逻辑上是不正确的。