SDL 2.0使用Blit绘制BMP图像仅第二次启动窗口可正常显示问题
问题原因分析
你的问题核心是没有处理SDL窗口的重绘事件,具体可拆解为3个问题点:
- 返回值误用:
SDL_UpdateWindowSurface函数成功执行时返回0,失败返回负数,你直接将返回值赋值给控制循环退出的quit变量,属于潜在逻辑错误,不过这不是你遇到显示异常的核心原因。 - 未处理重绘事件(核心问题):通过
SDL_GetWindowSurface获取的窗口表面内存是由窗口系统管理的,遇到以下任意场景时表面内容都会自动失效,需要手动重新绘制:- 窗口被其他应用遮挡后重新露出
- 窗口被拖动、调整大小
- 窗口首次创建完成后系统触发的强制重绘请求
你的代码仅在初始化阶段执行了1次图像blit和窗口更新操作,事件循环中没有处理SDL_WINDOWEVENT_EXPOSED类型的重绘事件,首次运行时初始化绘制的内容会被系统后续的重绘请求清空,因此窗口显示空白。
- 多窗口偶现显示的原因:短时间内多次运行程序创建同规格窗口时,窗口系统可能复用了未完全释放的旧窗口表面缓存,刚好保留了之前绘制的图像内容,这属于未定义行为,不是稳定生效的逻辑。拖动窗口花屏也正是因为拖动触发了重绘请求,你没有重新绘制导致表面内容丢失。
修复方案
你需要在事件循环中添加重绘事件的处理逻辑,每次窗口需要重绘时重新把BMP图像blit到窗口表面并更新窗口,修复后的代码如下:
#include <iostream> #include <SDL.h> int main(int argc, char ** argv) { bool quit = false; SDL_Event event; if (SDL_Init(SDL_INIT_VIDEO) < 0) { return 1; } SDL_Window * gWindow = SDL_CreateWindow("SDL_Yo", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640,480,SDL_WINDOW_SHOWN ); if (gWindow == NULL) { SDL_Quit(); return 1; } SDL_Surface* gScreenSurface = SDL_GetWindowSurface( gWindow ); if( gScreenSurface == NULL ) { SDL_DestroyWindow(gWindow); SDL_Quit(); return 1; } SDL_Surface* gHelloWorld = SDL_LoadBMP("IMG_0004.bmp"); if( gHelloWorld == NULL ) { SDL_DestroyWindow(gWindow); SDL_Quit(); return 1; } // 初始绘制一次 SDL_BlitSurface( gHelloWorld, NULL, gScreenSurface, NULL ); SDL_UpdateWindowSurface( gWindow ); while (!quit) { SDL_WaitEvent(&event); switch (event.type) { case SDL_QUIT: quit = true; break; // 新增重绘事件处理 case SDL_WINDOWEVENT: if (event.window.event == SDL_WINDOWEVENT_EXPOSED) { SDL_BlitSurface( gHelloWorld, NULL, gScreenSurface, NULL ); SDL_UpdateWindowSurface( gWindow ); } break; } } // 主动释放资源 SDL_FreeSurface(gHelloWorld); SDL_DestroyWindow(gWindow); SDL_Quit(); return 0; }
额外说明
如果需要避免拖动窗口时的画面撕裂问题,也可以改用SDL渲染器(SDL_CreateRenderer)API进行绘制,渲染器会自动管理纹理的重绘逻辑,不需要手动处理暴露事件。
内容的提问来源于stack exchange,提问作者Ace Blackwell
相关产品推荐
相关产品推荐

