参考课程:“FFmpeg4.3--系列8--SDL2.0小白入门”
SDL2.0知识框架体系
SDL 视频渲染主要涉及到四个对象:
SDL_Window
SDL_Surface
SDL_Render
SDL_Texture
SDL_Event
初始化:
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
SDL_mixer
SDL_ttf
1. SDL 视频渲染相关对象
SDL 视频渲染主要涉及到四个对象:SDL_Window、SDL_Render、SDL_Texture和SDL_Surface。
SDL_Window代表的是窗口的逻辑概念,它是存放在主内存中的一个对象。当我们调用SDL API 创建窗口后,它并不会被显示出来。
SDL_Render 是渲染器,它也是主存中的一个对象。对Render操作时实际上分为两个阶段:
渲染阶段。在该阶段,用户可以画各种图形渲染到SDL_Surface或SDL_Texture 中;
显示阶段。参SDL_Texture为数据,通过OpenGL操作GPU,最终将 SDL_Surfce 或SDL_Texture中的数据输出到显示器上。
SDL_Render对象中有一个视频缓冲区,该缓冲区我们称之为SDL_Surface,它是按照像素存放图像的。我们一般把真彩色的像素称为RGB24数据。也就是说,每一个像素由24位组成,每8位代表一种颜色,像素的最终颜色是由RGB三种颜色混合而成的。
SDL_Texture 与SDL_Surface 相似,也是一种缓冲区。只不过它存放的不是真正的像素数据,而是存放的图像的描述信息。这些描述信息通过OpenGL、D3D 或 Metal等技术操作GPU,从而绘制出与SDL_Surface一样的图形,且效率更高。
2. 使用 SDL_Texute 渲染纹理的步骤
SDL提供了操作SDL_Texture的方法,使用SDL_Texute的基本步骤为:
创建一个 SDL_Texture。
渲染 Texture
Destory Texture
3. 使用SDL渲染纹理的核心API
a). 创建 SDL_Texture
SDL_Texture* SDL_CreateTexture(SDL_Renderer* renderer, // 渲染器
Uint32 format,// 渲染数据的格式,如YUV、RGB
int access,// Texture 类型,target、stream
int w,
int h)
下面说明一下重要参数:
format: 指明像素格式,可以是YUV,也可以是RGB
access: 指明Texture的类型。可以是 Stream(视频),也可以是Target一般的类型。
b). 渲染
int SDL_RenderCopy(SDL_Renderer* renderer,
SDL_Texture* texture,
const SDL_Rect* srcrect,
const SDL_Rect* dstrect)
下面说明一下重要参数:
srcrect: 指定 Texture 中要渲染的一部分。如果将 Texture全部输出,可以设置它为 NULL。
dstrect: 指定输出的空间大小。
c). 销毁 SDL_Texture
void SDL_DestroyTexture(SDL_Texture* texture)
纹理渲染实战:
复制代码
// SDL.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#includeextern "C" { #include "SDL.h" } int main(int argc, char* argv[]) { 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 = 50; renderer = SDL_CreateRenderer(window, -1, 0); 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, 0x00, 0x00, 0x00, 0x00); SDL_RenderClear(renderer); SDL_RenderDrawRect(renderer, &r); SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0x00); SDL_RenderFillRect(renderer, &r); SDL_SetRenderTarget(renderer, NULL); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); } SDL_DestroyRenderer(renderer); SDL_Quit(); return 0; }
各位学员:
非常抱歉,这节课录制到最后两分钟的时候,家里的小宝宝突然跑进来,叫了几声。
给您带来了不便,影响了您的体验,敬请谅解。
梅会东
2021.1.6
事件循环
大多数多媒体程序依靠事件系统来处理输入。
SDL为处理输入事件提供了灵活的API。
本质上,SDL将来自设备(如键盘,鼠标或控制器)的输入记录为事件,将它们存储在“事件队列”中。
您可以将此结构视为等待线 - 事件在线的后面排队并从线的前面取出。
在您的程序中,您将始终拥有一个事件(或“游戏”或“主”)循环来处理这些事件并根据输入运行您的程序。每次运行事件循环时,必须从事件队列中拉出每个事件(按顺序)以处理输入。这是通过函数SDL_PollEvent()完成的。此函数从队列中删除第一个事件,将值复制到SDL_Event类型的参数中。如果事件队列为空,则该函数将返回0。
轮询事件后,您可以在逻辑链中使用它来推断输入内容和响应方式。
SDL_Event ev;
bool running = true;
// Main loop
while ( running ) {
// Event loop
while ( SDL_PolLEvent( &ev ) != 0 ) {
// Test members of ev
}
// Wait before next frame
SDL_Delay(100);
}
SDL_Event
SDL_Event包含任何子事件之一。这可以通过使用联合来实现。union描述了结构中的几个互斥数据成员。这意味着子事件类型都存储在同一个内存中,因此SDL_Event可以灵活而不浪费空间。但是,这个系统使语法稍微笨拙 - 访问子事件数据,首先必须访问SDL_Event中的子事件。
例如,访问SDL_KeyboardEvent ..
SDL_Event evt;
SDL_PollEvent( &evt );
if (evt.type == SDL_KEYDOWN) {
switch ( evt.key.sym.sym ) { // Note evt.key accesses the real data,
// the SDL_KeyboardEvent
// ...
}
}
退出
当用户希望关闭程序时, 您的事件循环将收到SDL_QUIT类型的事件。这包括按下窗口上的“x”,按ALT + F4,或以其他方式请求程序结束。这不包括结束进程或将CTRL + C发送到控制台 - 这些是不受控制的,立即中止。
因此,当您的程序收到SDL_QUIT事件时,它应该正常关闭(或提示用户提供更多信息)。事件的类型可通过其“类型”成员访问。
SDL_Event ev; bool running = true; // Main loop while ( running ) { // Event loop while ( SDL_PolLEvent( &ev ) != 0 ) { // check event type switch (ev.type) { case SDL_QUIT: // shut down running = false; break; } } // Wait before next frame SDL_Delay(100); } #include#include #undef main int main(int, char**)///main__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; }