SDL 学习笔记
目录
SDL 学习笔记
测试案例
显示图片案例
纹理与渲染案例
纹理与渲染图片案例
加载PNG图片
播放音频示例(使用扩展库SDL_mixer)
播放YUV视频示例
播放自定义YUV数据
事件机制
- 下载:http://libsdl.org/download-2.0.php
- https://www.libsdl.org/release/
- SDL_image 2.0扩展库下载:https://www.libsdl.org/projects/SDL_image/
- 初始化:
- SDL_Init(): 初始化 SDL。
- SDL_CreateWindow(): 创建窗口(Window)。
- SDL_CreateRenderer(): 基于窗口创建渲染器(Render)。
- SDL_CreateTexture(): 创建纹理(Texture)。
- 循环渲染数据:
- SDL_UpdateTexture(): 设置纹理的数据。
- SDL_RenderCopy(): 纹理复制给渲染器。
- SDL_RenderPresent(): 显示。
- 退出:
- SDL_DestroyWindow()
- SDL_DestroyRenderer();
- SDL_DestroyTexture();
- SDL_Quit();
- SDL 的“标准库”
- SDL_image—支持时下流行的图像格式:BMP、PPM、XPM、 PCX、GIF、JPEG、PNG、TGA。
- SDL_mixer—更多的声音输出函数以及更多的声音格式支持。
- SDL_net—网络支持。
- SDL_ttf—TrueType 字体渲染支持。
- SDL_rtf—简单的 RTF 渲染支持。
- 测试程序1:
#include
extern "C" { #include "SDL.h" } #undef main int main() { printf("Initialize SDL\n"); if (SDL_Init(SDL_INIT_EVERYTHING) == -1) return(1); printf("Delay 2 seconds\n"); SDL_Delay(2000); printf("Quit SDL\n"); SDL_Quit(); return 0; }
- 输出:
- 注:qt 搭建根据具体的相对路径,这里是
INCLUDEPATH+=../SDL2/include LIBS += -L../SDL2/lib/x86 -lSDL2 -lSDL2_image -lSDL2_mixer
#include
extern "C" { #include "SDL.h" } #undef main const int SCREEN_WIDTH = 640; const int SCREEN_HEIGHT = 480; int main() { //The window we'll be rendering to SDL_Window* gWindow = NULL; //The surface contained by the window SDL_Surface* gScreenSurface = NULL; //The image we will load and show on the screen SDL_Surface* gHelloWorld = NULL; //首先初始化 初始化SD视频子系统 if (SDL_Init(SDL_INIT_VIDEO) < 0) { printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); return false; } //创建窗口 gWindow = SDL_CreateWindow("SHOW BMP",//窗口标题 SDL_WINDOWPOS_UNDEFINED,//窗口位置设置 SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,//窗口的宽度 SCREEN_HEIGHT,//窗口的高度 SDL_WINDOW_SHOWN//显示窗口 ); if (gWindow == NULL) { printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); return false; } //Use this function to get the SDL surface associated with the window. //获取窗口对应的surface gScreenSurface = SDL_GetWindowSurface(gWindow); //加载图片 gHelloWorld = SDL_LoadBMP("./Hello_World.bmp");//加载图片 if (gHelloWorld == NULL) { printf("Unable to load image %s! SDL Error: %s\n", "Hello_World.bmp", SDL_GetError()); return false; } //Use this function to perform a fast surface copy to a destination surface. //surface的快速复制 //下面函数的参数分别为: SDL_Surface* src ,const SDL_Rect* srcrect , SDL_Surface* dst , SDL_Rect* dstrect SDL_BlitSurface(gHelloWorld, NULL, gScreenSurface, NULL); SDL_UpdateWindowSurface(gWindow);//更新显示copy the window surface to the screen SDL_Delay(2000);//延时2000毫秒,2s后自动关闭 //释放内存 SDL_FreeSurface(gHelloWorld);//释放空间 gHelloWorld = NULL; SDL_DestroyWindow(gWindow);//销毁窗口 gWindow = NULL; SDL_Quit();//退出SDL return 0; }
- 显示:
- SDL 视频渲染主要涉及到四个对象:
- SDL_Window、SDL_Render、SDL_Texture 和 SDL_Surface。
- 初始化:
- SDL_Init(): 初始化 SDL。
- SDL_CreateWindow(): 创建窗口(Window)。
- SDL_CreateRenderer(): 基于窗口创建渲染器(Render)。
- SDL_CreateTexture():
- 创建纹理(Texture)。
- 循环渲染数据:
- SDL_UpdateTexture():
- 设置纹理的数据。
- SDL_RenderCopy(): 纹理复制给渲染器。
- SDL_RenderPresent(): 显示。
- 使用 SDL_Texture 将视频纹理渲染出来。
#include
extern "C" { #include "SDL.h" } #undef main int main() { SDL_Window *window; SDL_Renderer *renderer; SDL_Texture *texture; SDL_Event event; SDL_Rect r; if (SDL_Init(SDL_INIT_VIDEO) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError()); return 3; } window = SDL_CreateWindow("SDL_CreateTexture", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 768, SDL_WINDOW_RESIZABLE); r.w = 100; r.h = 100; /// render---->window renderer = SDL_CreateRenderer(window, -1, 0); /// texture-->render texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 1024, 768); // 跳来跳去的方块 while (1) { SDL_PollEvent(&event); if (event.type == SDL_QUIT) break; r.x = rand() % 500; r.y = rand() % 500; SDL_SetRenderTarget(renderer, texture); SDL_SetRenderDrawColor(renderer, 0x88, 0x88, 0x88, 0x00); /// 将上一次render中的内容清空 SDL_RenderClear(renderer); SDL_RenderDrawRect(renderer, &r); SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0x00); SDL_RenderFillRect(renderer, &r); SDL_SetRenderTarget(renderer, NULL); /// core: 复制内容 SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); SDL_Delay(1000); } /// 清理、释放 SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; }
#include "SDL.h" } #undef main int main() { SDL_Init(SDL_INIT_VIDEO); SDL_Window*window = SDL_CreateWindow( "显示bmp图片", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN); SDL_Renderer*renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED); SDL_RenderClear(renderer); SDL_Surface*surface = SDL_LoadBMP("./Hello_world.bmp"); SDL_Rect box = { 0, 0, surface->w - 50, surface->h - 50 }; /// 将 图片的surface 关联到 一个新的 纹理中 SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); /// 复制 纹理中数据 到 渲染器 SDL_RenderCopy(renderer, texture, &box, &box); /// 刷新 渲染器 SDL_RenderPresent(renderer); SDL_Delay(2000); /// 清理、释放 SDL_FreeSurface(surface); SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; }
- 1. 初始化
- 2. 创建窗口:CreateWindow
- 3. 创建 渲染器(window)
- 4. 加载 图片(“path”)(IMG_XXX-->Init, Load, Quit)
- 5. 创建 纹理(render, bmp)
- 6. 复制 纹理 到 渲染器 RenderCopy
- 7. 刷新 渲染器(RenderPresent)
- 8. 退出(释放、清理)
#include
extern "C" { #include "SDL.h" #include "SDL_image.h" } #undef main int main() { bool quit = false; SDL_Event event; //SDL初始化,这里只显示图片,所以只初始化VIDEO系统,更多的支持查看官方文档 SDL_Init(SDL_INIT_VIDEO); //为了显示png图片,额外使用了图片库,所以要单独初始化 IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG); //建立SDL窗口 SDL_Window * window = SDL_CreateWindow("SDL2 Displaying Image", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0); //渲染层 SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0); //如果只是显示一张bmp图片,使用sdl内置的功能即可 //SDL_Surface * image = SDL_LoadBMP("only_support_BMP.bmp"); //因为要显示png图片,所以使用了外部库,sdl_image库当前支持jpg/png/webp/tiff图片格式 SDL_Surface * image = IMG_Load("Hello_world.png"); //载入的图片生成SDL贴图材质 SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, image); while (!quit) {//主消息循环 SDL_WaitEvent(&event); switch (event.type) { //用户从菜单要求退出程序 case SDL_QUIT: quit = true; break; } //如果指定显示位置使用下面注释起来的两句 //SDL_Rect dstrect = { 5, 5, 320, 240 }; //SDL_RenderCopy(renderer, texture, NULL, &dstrect); //把贴图材质复制到渲染器 SDL_RenderCopy(renderer, texture, NULL, NULL); //显示出来:刷新渲染器 SDL_RenderPresent(renderer); } //典型的三明治结构,清理各项资源 SDL_DestroyTexture(texture); SDL_FreeSurface(image); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); //退出image库 IMG_Quit(); //退出SDL SDL_Quit(); return 0; }
- 1. 初始化: Mix_Chunk
- 2. 打开音频: Mix_OpenAudio
- 3. 加载音频文件 wav:Mix_LoadWAV
- 4. 播放音频:Mix_PlayChannel,Mix_Playing
- 5. 退出(清理、释放):Mix_CloseAudio, Mix_FreeChunk
#include
extern "C" { #include "SDL.h" #include "SDL_mixer.h" } #undef main int main() { SDL_Surface *screen = NULL; //Pointer to the main screen surface Mix_Chunk *sound = NULL; //Pointer to our sound, in memory int channel; //Channel on which our sound is played int audio_rate = 22050; //Frequency of audio playback Uint16 audio_format = AUDIO_S16SYS; //Format of the audio we're playing int audio_channels = 2; //2 channels = stereo int audio_buffers = 4096; //Size of the audio buffers in memory //Initialize BOTH SDL video and SDL audio if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) { printf("Unable to initialize SDL: %s\n", SDL_GetError()); return 1; } //Initialize SDL_mixer with our chosen audio settings if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) != 0) { printf("Unable to initialize audio: %s\n", Mix_GetError()); exit(1); } //Load our WAV file from disk sound = Mix_LoadWAV("sound.wav"); if (sound == NULL) { printf("Unable to load WAV file: %s\n", Mix_GetError()); } //Set the video mode to anything, just need a window SDL_Window *pWindow = SDL_CreateWindow("My Game Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_SHOWN); if (pWindow) { screen = SDL_GetWindowSurface(pWindow); } if (screen == NULL) { printf("Unable to set video mode: %s\n", SDL_GetError()); return 1; } //Play our sound file, and capture the channel on which it is played channel = Mix_PlayChannel(-1, sound, 0); if (channel == -1) { printf("Unable to play WAV file: %s\n", Mix_GetError()); } //Wait until the sound has stopped playing while (Mix_Playing(channel) != 0); //Release the memory allocated to our sound Mix_FreeChunk(sound); //Need to make sure that SDL_mixer and SDL have a chance to clean up Mix_CloseAudio(); SDL_Quit(); //Return success! return 0; }
- 示例2
Uint8* g_audio_chunk; Uint32 g_audio_len; Uint8* g_audio_pos; const static uint32_t PCM_BUFFER_SIZE = 4096; //ffplay.exe -ar 44100 -ac 2 -f s16le -i xxx_44.1k_s16le.pcm const static std::string PCM_PATH = "gu10s-s16le-44100.pcm"; //数据到来的回调函数 void read_audio_data_cb(void* udata, Uint8* stream, int len) { SDL_memset(stream, 0, len); if (g_audio_len == 0) return; len = std::min(len, static_cast
(g_audio_len)); /// 将pcm音频数据送到声卡,最后一个参数控制 音量 SDL_MixAudio(stream, g_audio_pos, len, SDL_MIX_MAXVOLUME/2);// 0---128 g_audio_pos += len; g_audio_len -= len; } int mian_MixAudio() { SDL_Init(SDL_INIT_AUDIO); //init SDL_AudioSpec spec; spec.freq = 44100; spec.format = AUDIO_S16SYS; spec.channels = 2; spec.samples = 1024; spec.callback = read_audio_data_cb; spec.userdata = NULL; //根据参数,打开音频设备 if (SDL_OpenAudio(&spec, NULL) < 0) return -1; //打开文件 std::ifstream pcm(PCM_PATH.c_str(), std::ios::in | std::ios::binary); if (!pcm.is_open()) return -1; char* pcm_buffer = (char*)malloc(PCM_BUFFER_SIZE); //开始播放 SDL_PauseAudio(0); while (!pcm.eof()) { pcm.read(pcm_buffer, PCM_BUFFER_SIZE); if (pcm.bad()) break; g_audio_chunk = reinterpret_cast (pcm_buffer); g_audio_len = pcm.gcount(); //读取到的字节数 g_audio_pos = g_audio_chunk; std::cout << "play " << g_audio_len << " data" << std::endl; while (g_audio_len > 0) //等待audio_len长度的数据播放完成 SDL_Delay(1); } free(pcm_buffer); SDL_Quit(); return 0; }
#include
#include #include using namespace std; extern "C" { #include "SDL.h" } #undef main static const int width = 352; static const int height = 288; static SDL_Window *window = nullptr; static SDL_Renderer *ren = nullptr; int main(int argc, char **argv) { string filename = "background.bmp"; SDL_Event event; SDL_bool done = SDL_FALSE; Uint32 pixel_format = SDL_PIXELFORMAT_IYUV; int frame_number = 0; /// c语言函数:fopen, "rb":只读,二进制 /// 如果去掉“b”,文本模式读取,遇到“\0”,就会自动退出 FILE *fp = fopen("ande_352x288_yuv420p.yuv", "rb");//ande_352x288_yuv420p//out1_352x288 if (!fp) { return -1; } SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); //if (SDL_Init(SDL_INIT_EVERYTHING) != 0){ if (SDL_Init(SDL_INIT_VIDEO) != 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); return 3; } /// 创建窗口 SDL_Window *win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_SHOWN); if (win == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create window: %s\n", SDL_GetError()); return 1; } /// 创建渲染器 ren = SDL_CreateRenderer(win, 0, 0); //渲染器 底层,打底 if (ren == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create renderer: %s\n", SDL_GetError()); return 1; } /// 创建纹理 SDL_Texture *tex = SDL_CreateTexture(ren, pixel_format, SDL_TEXTUREACCESS_STREAMING, width, height); int len = height*width; Uint8* buf = (Uint8*)malloc(len * 2); int iPitch = width * SDL_BYTESPERPIXEL(pixel_format); printf("IPath %d\n", iPitch); SDL_Rect rec; rec.x = 0; rec.y = 0; rec.w = width; rec.h = height; int nRets = 0; while (!done) { while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_ESCAPE) { done = SDL_TRUE; } break; case SDL_QUIT: done = SDL_TRUE; break; } } /// 注意:每次需要的字节数: width*height*1.5 /// Y: width*height: Bytes /// cb: width*height / 4 /// cr: width*height / 4 nRets = fread(buf, 1, height*width * 3 / 2, fp); if (nRets < 0) { break; } //printf("lee = %d\n",lee); /// 用buf缓冲区数据 更新 纹理 SDL_UpdateTexture(tex, NULL, buf, iPitch); /* 刷新渲染器:三部曲:clear, copy, present */ SDL_RenderClear(ren); SDL_RenderCopy(ren, tex, NULL, &rec); SDL_RenderPresent(ren); frame_number++; /// 音视频同步: 休眠 SDL_Delay(40); 1S = 1000ms, 1000 / 40 = 25 fps(帧率:每秒25帧) } /// 退出:清理资源 SDL_Delay(2000); SDL_DestroyTexture(tex); SDL_DestroyRenderer(ren); SDL_DestroyWindow(win); SDL_Quit(); fclose(fp); return 0; }
#include
#include extern "C" { #include "SDL.h" } #undef main #pragma comment(lib, "SDL2.lib") #pragma comment(lib, "SDL2main.lib") /* Prepare a dummy image.:填充随机颜色 */ static void FillYuvImage(BYTE* pYuv, int nWidth, int nHeight, int nIndex) { int x, y, i; i = nIndex; BYTE* pY = pYuv; BYTE* pU = pYuv + nWidth * nHeight; BYTE* pV = pYuv + nWidth * nHeight * 5 / 4; /* Y */ for (y = 0; y < nHeight; y++) { for (x = 0; x < nWidth; x++) { pY[y * nWidth + x] = x + y + i * 2;//+ y } } /* Cb and Cr */ for (y = 0; y < nHeight / 2; y++) { for (x = 0; x < nWidth / 2; x++) { pU[y * (nWidth / 2) + x] = 64 + y + i * 2; pV[y * (nWidth / 2) + x] = 64 + x + i * 5; } } } int main(int argc, char* argv[]) { if (SDL_Init(SDL_INIT_VIDEO)) { printf("Could not initialize SDL - %s\n", SDL_GetError()); return -1; } // 提升图像质量,否则默认缩放质量会有毛剌 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); SDL_Window* window = SDL_CreateWindowFrom(::GetConsoleWindow()); SDL_Renderer* render = SDL_CreateRenderer(window, -1, 0); const int W = 1920; const int H = 1080; SDL_Texture* texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, W, H); /// yuv 420P : planer static BYTE Yuv[W*H * 2]; BYTE* pY = Yuv; BYTE* pU = Yuv + W*H; BYTE* pV = Yuv + W*H * 5 / 4; int index = 0; while (true) { FillYuvImage(Yuv, W, H, index++);///填充随机颜色 int e = SDL_UpdateYUVTexture(texture, NULL, pY, W, pU, W / 2, pV, W / 2); /// 刷新渲染器:三部曲: clear, copy, present SDL_RenderClear(render); SDL_RenderCopy(render, texture, NULL, NULL); SDL_RenderPresent(render); Sleep(40); } SDL_DestroyTexture(texture); SDL_DestroyRenderer(render); //SDL_DestroyWindow(window); SDL_Quit(); return 0; }
- 示例1:
#include
#undef main int main__event2(int, char**)///__event2 { SDL_Init(SDL_INIT_EVERYTHING); SDL_Window* win = SDL_CreateWindow("yx3sxmeng", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN); SDL_Renderer* renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED); SDL_Surface* surface = SDL_LoadBMP("./Hello_world.bmp"); SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); ///1. window-->render(GPU, window), surface内存信息(bitmap), texture描述信息(render, bitmap|surface); ///2. SDL事件循环机制 bool quit = false; SDL_Event ev; SDL_Rect rect = { 0,0,800,600 };///目标位置大小(x,y, w,h); int sx = 0, sy = 0; while (!quit) { while (SDL_PollEvent(&ev)) { switch (ev.type) { case SDL_QUIT: quit = true; break; case SDL_MOUSEBUTTONDOWN: sx = ev.button.x + rect.x; sy = ev.button.y + rect.y; break; case SDL_MOUSEMOTION: if (ev.motion.state & SDL_BUTTON_LMASK) { rect.x = ev.motion.x - sx; rect.y = ev.motion.y - sy; } break; case SDL_KEYDOWN: if (ev.key.keysym.sym == SDLK_LEFT) { rect.x -= 10; printf("SDLK_LEFT..."); } else if (ev.key.keysym.sym == SDLK_RIGHT) { printf("SDLK_RIGHT..."); rect.x += 10; } else if (ev.key.keysym.sym == SDLK_UP) { printf("SDLK_UP..."); rect.w += 10; rect.h += 10; } else if (ev.key.keysym.sym == SDLK_DOWN) { printf("SDLK_DOWN..."); rect.w -= 10; rect.h -= 10; } printf("scancode=%d\n", ev.key.keysym.scancode); break; case SDL_MOUSEWHEEL: if (ev.wheel.y > 0) { rect.h *= 1.1; rect.w *= 1.1; } if (ev.wheel.y < 0) { rect.w /= 1.1; rect.h /= 1.1; } break; } } SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, &rect); SDL_RenderPresent(renderer); SDL_Delay(16); } //释放资源 SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(win); SDL_Quit(); return 0; }