SDL基础
SDL
SDL(Simple DirectMedia Layer)是一个开源的跨平台多媒体开发库,主要用于开发需要图形、音频和输入设备支持的应用程序。它使用C语言编写,提供了简单易用的API,**能够帮助开发者快速实现跨平台的多媒体功能。**SDL广泛应用于游戏开发、模拟器、媒体播放器等领域。
SDL的主要特点包括:
- 跨平台支持:兼容Windows、macOS、Linux、iOS、Android等多种操作系统。
- 多媒体功能:提供图形渲染、音频处理、输入设备管理等功能。
- 简单易用:API设计简洁,易于上手,适合初学者和专业开发者。
- 社区支持:拥有活跃的开源社区,提供丰富的文档、教程和扩展库。
视频播放器:结合SDL的窗口和渲染功能以及FFmpeg的解码能力,可以开发出跨平台的视频播放器。
实验一 创建窗口
SDL_Init(int flag):初始化SDL系统
好的!以下是将您提供的SDL初始化标志整理成表格的形式,方便查看和理解:
标志名称 | 描述 |
---|---|
SDL_INIT_TIMER | 初始化定时器子系统。 |
SDL_INIT_AUDIO | 初始化音频子系统。 |
SDL_INIT_VIDEO | 初始化视频子系统(包括窗口和渲染器)。 |
SDL_INIT_JOYSTICK | 初始化游戏手柄子系统。 |
SDL_INIT_HAPTIC | 初始化力反馈设备子系统。 |
SDL_INIT_GAMECONTROLLER | 初始化游戏控制器子系统。 |
SDL_INIT_EVENTS | 初始化事件子系统。 |
SDL_INIT_EVERYTHING | 初始化所有子系统。 |
SDL_CreateWindow():创建窗口SDL_Window
SDL_Window* SDL_CreateWindow(const char* title, int x, int y, int w, int h, Uint32 flags)
title:窗口的标题,显示在窗口的标题栏上。
x:窗口在屏幕上的水平位置。可以使用SDL_WINDOWPOS_UNDEFINED让系统自动选择位置。
y:窗口在屏幕上的垂直位置。可以使用SDL_WINDOWPOS_UNDEFINED让系统自动选择位置。
w:窗口的宽度,以像素为单位。
h:窗口的高度,以像素为单位。
当然可以,以下是您需要的表格:
标志名称 | 描述 |
---|---|
SDL_WINDOW_SHOWN | 创建窗口后立即显示。 |
SDL_WINDOW_FULLSCREEN | 创建全屏窗口。 |
SDL_WINDOW_BORDERLESS | 创建无边框窗口。 |
SDL_WINDOW_RESIZABLE | 创建可调整大小的窗口。 |
SDL_WINDOW_MAXIMIZED | 创建最大化窗口。 |
SDL_WINDOW_MINIMIZED | 创建最小化窗口。 |
SDL_WINDOW_HIDDEN | 创建隐藏窗口。 |
**SDL_Delay():**工具函数,用于延时
ms:暂停的时间,以毫秒为单位。
**SDL_Quit():**退出SDL系统
代码
printf("Hello World!\n");SDL_Window *window = NULL; // 声明窗口SDL_Init(SDL_INIT_VIDEO); // 初始化SDL// 创建SDL Windowwindow = SDL_CreateWindow("Basic Window",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,640,480,SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);if(!window) // 检测是否创建成功{printf("Can't create window, err:%s\n", SDL_GetError());return 1;}SDL_Delay(10000); // 延迟10000msSDL_DestroyWindow(window); // 消耗窗口SDL_Quit(); // 释放资源return 0;
当设置为 SDL_WINDOWPOS_UNDEFINED 时,表示窗口的初始位置由操作系统决定,通常会根据当前的桌面布局和系统设置自动放置窗口
实验二 渲染器和纹理器
渲染器(Renderer)
渲染器是SDL中用于将图像绘制到窗口或屏幕上的核心组件。它提供了一个统一的接口,允许开发者使用硬件加速来渲染2D图形。渲染器的主要功能包括:
- 创建纹理:渲染器可以创建纹理对象,用于存储图像数据。
- 绘制纹理:渲染器将纹理绘制到窗口上。
- 清除屏幕:渲染器可以清除屏幕内容,为新的绘制操作做准备。
- 更新显示:渲染器将绘制的内容显示到屏幕上。
SDL_Renderer* SDL_CreateRenderer(SDL_Window* window, int index, Uint32 flags);
window:指向 SDL_Window 对象的指针,指定渲染器将在哪个窗口上绘制。
index:指定渲染驱动程序的索引。通常使用 -1,表示使用默认的渲染驱动程序。
flags:指定渲染器的创建标志,用0就行
纹理(Texture)
纹理是存储在显存中的图像数据,利用硬件加速可以高效地进行绘制。纹理的主要功能包括:
- 存储图像数据:纹理可以存储从文件加载的图像数据,或者动态生成的图像数据。
- 高效渲染:纹理可以被快速渲染到屏幕上,适合频繁更新的场景。
SDL_Texture* SDL_CreateTexture(SDL_Renderer *renderer, Uint32 format, int access, int w, int h);
renderer:指向 SDL_Renderer 对象的指针。
format:像素格式,例如 RGB 或 YUV。
access:指定纹理的访问模式,例如 SDL_TEXTUREACCESS_STREAMING 或 SDL_TEXTUREACCESS_TARGET。
(创建可作为渲染目标的纹理:在调用 SDL_CreateTexture 创建纹理时,将 access 参数设置为 SDL_TEXTUREACCESS_TARGET,即可创建一个可以作为渲染目标的纹理。)
w 和 h:纹理的宽度和高度。
SDL_Rect 一个简单的矩形结构
int main(){int run = 1;SDL_Window *window = NULL;SDL_Renderer *renderer = NULL;SDL_Texture *texture = NULL;SDL_Rect rect; // 长方形,原点在左上角rect.w = 50; //方块大小rect.h = 50;SDL_Init(SDL_INIT_VIDEO);//初始化函数,可以确定希望激活的子系统window = SDL_CreateWindow("2 Window",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,640,480,SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);// 创建窗口if (!window){return -1;}renderer = SDL_CreateRenderer(window, -1, 0);//基于窗口创建渲染器if (!renderer){return -1;}texture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET,640,480); //创建纹理if (!texture){return -1;}int show_count = 0;while (run){rect.x = rand() % 600;rect.y = rand() % 400;SDL_SetRenderTarget(renderer, texture); // 设置渲染目标为纹理SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // 纹理背景为黑色SDL_RenderClear(renderer); //清屏->之前显示清掉SDL_RenderDrawRect(renderer, &rect); //绘制一个长方形SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); //长方形为白色 rgb ->透明成都 SDL_RenderFillRect(renderer, &rect);SDL_SetRenderTarget(renderer, NULL); //恢复默认,渲染目标为窗口SDL_RenderCopy(renderer, texture, NULL, NULL); //拷贝纹理到CPUSDL_RenderPresent(renderer); //输出到目标窗口上SDL_Delay(300);if(show_count++ > 30){run = 0; // 不跑了}}SDL_DestroyTexture(texture);SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window); //销毁窗口SDL_Quit();return 0;}
一个窗口如果只有一个渲染器,那么整个窗口都是渲染器范围
如果在使用 SDL_RenderCopy(renderer, texture, NULL, NULL); 绘制纹理时省略了源矩形(srcrect)和目标矩形(dstrect),SDL会默认执行以下操作:
源矩形(srcrect):默认为 NULL,表示使用纹理的整个区域。 目标矩形(dstrect):默认为
NULL,表示纹理会被拉伸或压缩以适应渲染目标(窗口)的尺寸。
实验三 事件检测
SDL(Simple DirectMedia Layer)的事件系统是处理用户输入和系统事件的核心机制。它允许开发者检测和响应各种事件,如键盘按键、鼠标移动、窗口调整大小等
操作事件
- SDL_WaitEvent():等待一个事件
- SDL_PushEvent():发送一个事件
事件 - SDL_QUIT:用户点击窗口关闭按钮时触发。
- SDL_KEYDOWN、SDL_KEYUP:键盘按键按下或释放时触发。
- SDL_MOUSEBUTTONDOWN、SDL_MOUSEBUTTONUP:鼠标按键按下或释放时触发。
- SDL_MOUSEMOTION:鼠标移动时触发。
- SDL_USEREVENT:用户自定义事件,开发者可以通过SDL_PushEvent向队列中插入自定义事件。
#define FF_QUIT_EVENT (SDL_USEREVENT + 2)
int main(){SDL_Window *window = NULL; // Declare a pointerSDL_Renderer *renderer = NULL;SDL_Init(SDL_INIT_VIDEO); // Initialize SDL2// Create an application window with the following settings:window = SDL_CreateWindow("An SDL2 window", // window titleSDL_WINDOWPOS_UNDEFINED, // initial x positionSDL_WINDOWPOS_UNDEFINED, // initial y position640, // width, in pixels480, // height, in pixelsSDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS// flags - see below);// Check that the window was successfully createdif (window == NULL){// In the case that the window could not be made...printf("Could not create window: %s\n", SDL_GetError());return 1;}/* We must call SDL_CreateRenderer in order for draw calls to affect this window. */renderer = SDL_CreateRenderer(window, -1, 0);/* Select the color for drawing. It is set to red here. */SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);/* Clear the entire screen to our selected color. */SDL_RenderClear(renderer);/* Up until now everything was drawn behind the scenes.This will show the new, red contents of the window. */SDL_RenderPresent(renderer);SDL_Event event;int b_exit = 0;for (;;){SDL_WaitEvent(&event);//检测事件switch (event.type){case SDL_KEYDOWN: /* 键盘事件 */switch (event.key.keysym.sym){case SDLK_a:printf("key down a\n");break;case SDLK_s:printf("key down s\n");break;case SDLK_d:printf("key down d\n");break;case SDLK_q:printf("key down q and push quit event\n");SDL_Event event_q;event_q.type = FF_QUIT_EVENT;SDL_PushEvent(&event_q);break;default:printf("key down 0x%x\n", event.key.keysym.sym);break;}break;case SDL_MOUSEBUTTONDOWN: /* 鼠标按下事件 */if (event.button.button == SDL_BUTTON_LEFT){printf("mouse down left\n");}else if(event.button.button == SDL_BUTTON_RIGHT){printf("mouse down right\n");}else{printf("mouse down %d\n", event.button.button);}break;case SDL_MOUSEMOTION: /* 鼠标移动事件 */printf("mouse movie (%d,%d)\n", event.button.x, event.button.y);break;case FF_QUIT_EVENT://printf("receive quit event\n");b_exit = 1;break;}if(b_exit)break;}//destory rendererif (renderer)SDL_DestroyRenderer(renderer);// Close and destroy the windowif (window)SDL_DestroyWindow(window);// Clean upSDL_Quit();return 0;}
在这个例子中,FF_QUIT_EVENT 被定义为用户自定义事件类型,其值是 SDL_USEREVENT + 2。这意味着 FF_QUIT_EVENT 是第二个用户自定义事件(从 SDL_USEREVENT 开始计数)。
实验四 多线程
◼ SDL线程创建: SDL_CreateThread:无需阻塞 就能执行
◼ SDL线程等待: SDL_WaitThead
◼ SDL互斥锁: SDL_CreateMutex/SDL_DestroyMutex
◼ SDL锁定互斥: SDL_LockMutex/SDL_UnlockMutex
◼ SDL条件变量(信号量): SDL_CreateCond/SDL_DestoryCond
◼ SDL条件变量(信号量)等待/通知: SDL_CondWait/SDL_CondSinga //自动解锁
SDL_mutex *s_lock = NULL;
SDL_cond *s_cond = NULL;int thread_work(void *arg)
{SDL_LockMutex(s_lock);printf(" <============thread_work sleep\n");sleep(10); // 用来测试获取锁printf(" <============thread_work wait\n");// 释放s_lock资源,并等待signal。之所以释放s_lock是让别的线程能够获取到s_lockSDL_CondWait(s_cond, s_lock); //另一个线程(1)发送signal和(2)释放lock后,这个函数退出printf(" <===========thread_work receive signal, continue to do ~_~!!!\n");printf(" <===========thread_work end\n");SDL_UnlockMutex(s_lock);return 0;
int main(){s_lock = SDL_CreateMutex();s_cond = SDL_CreateCond();SDL_Thread * t = SDL_CreateThread(thread_work,"thread_work",NULL);//开始执行线程if(!t){printf(" %s",SDL_GetError);return -1;}for(int i = 0;i< 2;i++){sleep(2);printf("main execute =====>\n");}printf("main SDL_LockMutex(s_lock) before ====================>\n");SDL_LockMutex(s_lock); // 获取锁,但是子线程还拿着锁 4 秒printf("main ready send signal====================>\n");printf("main SDL_CondSignal(s_cond) before ====================>\n");SDL_CondSignal(s_cond); // 发送信号,唤醒等待的线程printf("main SDL_CondSignal(s_cond) after ====================>\n");sleep(10);SDL_UnlockMutex(s_lock);// 释放锁,让其他线程可以拿到锁printf("main SDL_UnlockMutex(s_lock) after ====================>\n");SDL_WaitThread(t, NULL);SDL_DestroyMutex(s_lock);SDL_DestroyCond(s_cond);return 0;}
main execute =====>
main execute =====>
main SDL_LockMutex(s_lock) before ============>
<thread_work wait
main ready send signal>
main SDL_CondSignal(s_cond) before ====================>
main SDL_CondSignal(s_cond) after ====================>
main SDL_UnlockMutex(s_lock) after ====================>
<===========thread_work receive signal, continue to do _!!!
<===========thread_work end