SDL_RenderReadPixels doesn't give the same data depending on if the app is fullscreen or not
If my app is on windowed then everything works fine, when I go to fullscreen, I get random garbage. Anything that's in the rectangle of pixel the function reads, is either white, orange (#FFA040), or cyan (#42E2FF) I sent the part of my code that output values in this order : pixel value, red, green, blue, alpha components and does collision with it. What's also weird is that I have to use SDL_PIXELFORMAT_ARGB8888 instead of SDL_PIXELFORMAT_RGBA8888 if I want it to work at all. Here are the results of my testing and the function that reads it (I'm making a color-based collision system).
What is going on ? Thanks for your answer. Version is SDL 2.30.4
fullscreen.txt not fullscreen.txt part of the code that does it.txt
Make sure you read before you call SDL_RenderPresent(). Also, this is going to be incredibly slow. You should use some other way of tracking collision for your game.
Yes, I do call it before SDL_RenderPresent(), in fact it also has to put the player sprite after reading a 7x10 pixels rectangle, and this is the only way I found to make collisions that didn't make me lose my sanity.
What platform are you running on and what renderer is being used?
I am running that on Android with a CPU renderer because the OS won't give it an accelerated one (I work within Termux), and I make tests with a friend running Windows 11 and on his PC it is using a GPU renderer. The same thing happens on both machines.
I'm thinking of doing a workaround that would just open a second window for fullscreen and copying the content from the first one to this one.
(sorry for the delay I was sleeping)
Something I would like to add : SDL3 is not available yet in the Termux repositories so I cannot make the switch for now
Can you post a simple example or a modification to the SDL tests that shows this bug?
While stitching together a quick-and-dirty example made out of my code and it not reproducing itself, I found out what was causing the bug. The pitch argument for SDL_RenderReadPixels won't scale up if a SDL_SetLogicalSize has been done before going into fullscreen. If I want to fix this, I need to obtain the new width, divide it by the old width and multiply it by the read area's width. I'm going to add it to the example code that I will send.
Here is the code, I've already set it to something that shows the bug (400x400 window with 200x200 logical size). Github doesn't like .c files so it's a .txt The only functions that matter are the main, checkcolorcollision and SDL_CalculatePitch. Others are event gestion and stuff like that. I got that last function somewhere online but I think it'd be a nice addition to the SDL lib itself.
Launch it via command line after compiling it, as it'll show in this order : Pixel value, r, g, b, a components and if the player collides with a certain attack. testinglebug.txt
FYI, the pitch is defined as the number of bytes between rows of your image, so in your case you shouldn't use the SDL_CalculatePitch() function, instead the value would be sizeof(colors) / positionplayer.h
This is unrelated to the bug, I thought it might be helpful to know.
Reading the surface back is going to be a performance problem later, but your issue is fixed in SDL3.
Here's your sample, converted to SDL3:
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
SDL_FRect positionplayer;
SDL_FRect positionboolet[3];
SDL_PixelFormat *format = NULL;
enum
{
KEY_Z,
KEY_X,
KEY_C,
KEY_UP,
KEY_DOWN,
KEY_LEFT,
KEY_RIGHT,
KEY_ESC,
KEY_R
};
int keyrepeat[9];
int speed = 2;
int quitting = 0;
void checkcolorcollision()
{
int i = 0;
Uint8 r, g, b, a;
SDL_Rect rect;
rect.x = (int)positionplayer.x;
rect.y = (int)positionplayer.y;
rect.w = (int)positionplayer.w;
rect.h = (int)positionplayer.h;
SDL_Surface *surface = SDL_RenderReadPixels(renderer, &rect);
if (!surface) {
// This happens when positionplayer is offscreen
//SDL_ShowSimpleMessageBox(0, "wat", SDL_GetError(), NULL);
return;
}
Uint32 *colors = (Uint32 *)surface->pixels;
while (i < positionplayer.w * positionplayer.h) {
SDL_GetRGBA(colors[i], surface->format, &r, &g, &b, &a);
//SDL_Log("\n%d %d %d %d %d\n", colors[i], r, g, b, a);
if (r == 255 && g == 255 && b == 255) {
SDL_Log("normal atacc \n");
return;
}
if (r == 0xFF && g == 0xA0 && b == 0x40) {
SDL_Log("orange atacc \n");
return;
}
if (r == 0x42 && g == 0xE2 && b == 0xFF) {
SDL_Log("blue atacc \n");
return;
}
i++;
}
SDL_DestroySurface(surface);
}
void countrepeats(const Uint8 *keypressed)
{
if (keypressed[SDL_GetScancodeFromKey(SDLK_Z, SDL_KMOD_NONE)]) {
keyrepeat[KEY_Z]++;
} else if (keyrepeat[KEY_Z]) {
keyrepeat[KEY_Z] = 0;
}
if (keypressed[SDL_GetScancodeFromKey(SDLK_C, SDL_KMOD_NONE)]) {
keyrepeat[KEY_C]++;
} else if (keyrepeat[KEY_C]) {
keyrepeat[KEY_C] = 0;
}
if (keypressed[SDL_GetScancodeFromKey(SDLK_Z, SDL_KMOD_NONE)]) {
keyrepeat[KEY_X]++;
} else if (keyrepeat[KEY_X]) {
keyrepeat[KEY_X] = 0;
speed = 2;
}
if (keypressed[SDL_GetScancodeFromKey(SDLK_LEFT, SDL_KMOD_NONE)]) {
keyrepeat[KEY_LEFT]++;
} else if (keyrepeat[KEY_LEFT]) {
keyrepeat[KEY_LEFT] = 0;
}
if (keypressed[SDL_GetScancodeFromKey(SDLK_RIGHT, SDL_KMOD_NONE)]) {
keyrepeat[KEY_RIGHT]++;
} else if (keyrepeat[KEY_RIGHT]) {
keyrepeat[KEY_RIGHT] = 0;
}
if (keypressed[SDL_GetScancodeFromKey(SDLK_UP, SDL_KMOD_NONE)]) {
keyrepeat[KEY_UP]++;
} else if (keyrepeat[KEY_UP]) {
keyrepeat[KEY_UP] = 0;
}
if (keypressed[SDL_GetScancodeFromKey(SDLK_DOWN, SDL_KMOD_NONE)]) {
keyrepeat[KEY_DOWN]++;
} else if (keyrepeat[KEY_DOWN]) {
keyrepeat[KEY_DOWN] = 0;
}
if (keypressed[SDL_GetScancodeFromKey(SDLK_ESCAPE, SDL_KMOD_NONE)] || keypressed[SDL_GetScancodeFromKey(SDLK_Q, SDL_KMOD_NONE)]) {
keyrepeat[KEY_ESC]++;
} else if (keyrepeat[KEY_ESC]) {
keyrepeat[KEY_ESC] = 0;
}
if (keypressed[SDL_GetScancodeFromKey(SDLK_R, SDL_KMOD_NONE)]) {
keyrepeat[KEY_R]++;
} else if (keyrepeat[KEY_R]) {
keyrepeat[KEY_R] = 0;
}
}
void eventgestion()
{
const Uint8 *keypressed = SDL_GetKeyboardState(NULL);
SDL_PumpEvents();
countrepeats(keypressed);
SDL_PumpEvents();
//SDL_Log("%d %d %d %d %d %d \n", keyrepeat[KEY_Z], keyrepeat[KEY_X], keyrepeat[KEY_UP], keyrepeat[KEY_DOWN], keyrepeat[KEY_LEFT], keyrepeat[KEY_RIGHT]);
if (keyrepeat[KEY_UP] > 0)
positionplayer.y -= speed;
if (keyrepeat[KEY_DOWN] > 0)
positionplayer.y += speed;
if (keyrepeat[KEY_LEFT] > 0)
positionplayer.x -= speed;
if (keyrepeat[KEY_RIGHT] > 0)
positionplayer.x += speed;
if (keyrepeat[KEY_ESC] > 0) {
quitting = 1;
}
}
int main(int argc, char *argv[])
{
SDL_Rect positionwindow = { 50, 50, 400, 400 };
int r[3] = { 0xff, 0xff, 0x42 };
int g[3] = { 0xff, 0xa0, 0xe2 };
int b[3] = { 0xff, 0x40, 0xff };
int i = 0;
window = SDL_CreateWindow("le bug", positionwindow.w, positionwindow.h, 0);
renderer = SDL_CreateRenderer(window, NULL);
SDL_SetRenderVSync(renderer, 1);
//Uncomment for fullscreen
//SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
SDL_SetRenderLogicalPresentation(renderer, 200, 200, SDL_LOGICAL_PRESENTATION_LETTERBOX, SDL_SCALEMODE_LINEAR);
positionplayer.w = 16;
positionplayer.h = 16;
positionplayer.x = 50;
positionplayer.y = 150;
positionboolet[0].w = 32;
positionboolet[0].h = 32;
positionboolet[0].x = 32;
positionboolet[0].y = 32;
positionboolet[1] = positionboolet[0];
positionboolet[1].x += 64;
positionboolet[2] = positionboolet[1];
positionboolet[2].x += 64;
while (!quitting) {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
i = 0;
while (i < 3) {
SDL_SetRenderDrawColor(renderer, r[i], g[i], b[i], 255);
SDL_RenderFillRect(renderer, &positionboolet[i]);
i++;
}
eventgestion();
checkcolorcollision();
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
SDL_RenderFillRect(renderer, &positionplayer);
SDL_Delay(10);
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
return 0;
}
Alright, thanks for the answer. I'll be waiting for the addition of SDL3 and its add-ons to the Termux repos.