一. 基本步驟
使用SDL渲染圖像的步驟基本可分為以下幾步:
1.1 初始化SDL接口
SDL_Init(SDL_INIT_VIDEO)
初始化SDL Video 庫, 成功返回0, 失敗返回非0值。
1.2 創建SDL窗口(可以直接創建一個窗口或是綁定一個窗口句柄)
這是生成窗口可以分為兩種:
第一種是獨立創建一個窗口:
SDL_Window *SDLCALL SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags);
其中,title代表的是窗口的標題,x, y 代表的是窗口的坐標, w, h 代表的是窗口的寬高,flags 代表的是窗口的格式,比如可以指定:SDL_WINDOW_OPENGL(使用OpenGL渲染), SDL_WINDOW_RESIZABLE(窗口大小可改變)。
第二種是可以指定使用一個窗口來創建:
SDL_Window *SDLCALL SDL_CreateWindowFrom(const void *data);
這里的void * 可以直接傳遞一個QT的窗口句柄。
1.3 創建渲染器
SDL_CreateRenderer(SDL_Window *window, int index, Uint32 flags);
第一個參數就是SDL 窗口的句柄,用於指定渲染器創建在哪個窗口上;第二個代表渲染器驅動的索引,不理解的話直接傳-1就行,第三個參數代表渲染模式,可以指定一些相應的渲染模式,比如:
- SDL_RENDERER_SOFTWARE, 軟件渲染;
- SDL_RENDERER_ACCELERATED, 硬件加速渲染;
- SDL_RENDERER_PRESENTVSYNC, 與顯示器同步;
- SDL_RENDERER_TARGETTEXTURE, 渲染到材質
硬件加速可能在某些平台中不支持,如果不支持話,可以選擇 SDL_RENDERER_SOFTWARE,這個一般都支持。
1.4 在渲染器當中創建一個材質
材質就是用於存儲我們要顯示的具體的圖像信息, 它將來會放置到顯存當中
SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, w, h);
第一個參數代表渲染器對象,第二個參數代表像素格式,比如上面傳輸的ARGB8888這種像素格式, 第三個參數代表材質的類型,可選參數如下:
- SDL_TEXTUREACCESS_STATIC, 不需要頻繁修改,不需要鎖定
- SDL_TEXTUREACCESS_STREAMING, 需要頻繁修改,需要鎖定
- SDL_TEXTUREACCESS_TARGET, ?,暫時不理解
后面兩個參數 w, h 就是代表材質的寬高。
1.5 將內存數據寫入材質
我們初始的圖像肯定是位於內存當中的,不管是自己創建的圖像數據,還是從ffmpeg解碼得到的圖像,都是在內存當中,那要把內存當中的數據轉到顯存當中,就要用到此接口進行轉換。該函數的作用其實就是把內存當中的值更新到Texture,也就是顯存當中。
int SDLCALL SDL_UpdateTexture(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch)
第一個參數代表材質對象,就是更新到哪個材質當中;第二個參數代表坐標,也就是將要存進來的圖像數據,我們取這幅圖像的哪些位置,傳NULL的話就代表全部取,就是取全部圖像數據;第三個參數代表具體的圖像數據,第四個參數代表我們一會兒取得的圖像數據,每一行的字節數。
接下來就是具體的渲染操作了,也就是顯示,顯示需要分為以下幾步:
1.6 清理屏幕
SDL_RenderClear(SDL_Renderer *renderer)
不用說,參數就是代表渲染器對象。
1.7 復制材質到渲染器對象
SDL_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_Rect *dstrect);
第一個參數代表渲染器對象,第二個參數代表需要copy的材質對象,第三個參數代表取原始圖像的什么位置和尺寸,傳NULL代表取原始圖像的所有部分;第四個參數代表在渲染區(窗口)的哪個位置顯示。這個函數可以用於顯示全部材質,也就是截取材質當中的一部分進行顯示。
1.8 執行渲染操作
SDL_RenderPresent(SDL_Renderer *renderer);
參數就是渲染器對象。
下面我們自己准備一個RGB 數據,然后使用SDL 渲染出來。
二. 使用SDL渲染一幅RGB圖像數據
使用VS2019 創建一個空項目命名為sdl_rgb,准備好之前編譯生成的SDL庫文件和頭文件,然后做如下配置:
右鍵項目名-”屬性“-”C/C++“-”附加包含目錄“, 添加包含目錄:..\..\include
然后點擊”鏈接器“-”附加庫目錄“,添加庫目錄:..\..\lib\x86
設置輸出路徑:”常規“-”輸出目錄“, ..\..\bin\x86
設置調試的工具目錄:”調試“-”工作目錄“, ..\...\bin\x86
點”確定“,完成項目的初始配置。
打開創建好的cpp文件,輸入如下code:
#include <iostream> #include <sdl/SDL.h> using namespace std; #pragma comment(lib, "SDL2.lib") // SDL 庫里面定義了一個宏 main, 這里給取消掉,否則會與main 函數名沖突,導致編譯錯誤 #undef main int main() { // 定義圖像的寬高 int w = 800; int h = 600; // 1. 初始化SDL庫, 成功返回0, 失敗返回非0值 if (SDL_Init(SDL_INIT_VIDEO)) { cout << SDL_GetError() << endl; return -1; } // 2. 創建SDL窗口 auto screen = SDL_CreateWindow("test_sdl_ffmpeg", // 窗口標題 SDL_WINDOWPOS_CENTERED, // 窗口位置 SDL_WINDOWPOS_CENTERED, w, h, // 窗口寬高 SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE // 窗口屬性,指定使用OpenGL, 並且可調整大小 ); if (!screen) { cout << SDL_GetError() << endl; return -2; } // 3. 創建渲染器 auto render = SDL_CreateRenderer(screen, // 指定渲染到哪個窗口 -1, // 指定渲染器驅動,默認傳-1 SDL_RENDERER_ACCELERATED // 指定渲染模式,這里采用硬件加速模式 ); if (!render) { cout << SDL_GetError() << endl; return -3; } // 4. 在渲染器當中創建一個材質 auto texture = SDL_CreateTexture(render, // 指定在哪個渲染器當中創建 SDL_PIXELFORMAT_ARGB8888, // 指定當前材質的像素格式 SDL_TEXTUREACCESS_STREAMING, // 設定當前材質可修改 w, h // 指定材質寬高 ); if (!texture) { cout << SDL_GetError() << endl; return -4; } // 准備一幅800*600的紅色RGB圖像數據 shared_ptr<unsigned char> rgb(new unsigned char[w * h * 4]); // 乘以4是因為像素格式已指定為ARGB888,單個像素點占4字節 auto r = rgb.get(); // 為上述圖像數據賦值 for (int j = 0; j < h; j++) { int lineR = j * w * 4; // 每一行R分量的起始位置 for (int i = 0; i < w * 4; i += 4) { r[lineR + i] = 0; // B r[lineR + i + 1] = 0; // G r[lineR + i + 2] = 255; // R r[lineR + i + 3] = 0; // A } } // 5. 將內存中的RGB數據寫入材質 if (SDL_UpdateTexture(texture, NULL, r, w * 4)) { cout << SDL_GetError() << endl; return -5; } // 6. 清理渲染區(清理屏幕) if (SDL_RenderClear(render)) { cout << SDL_GetError() << endl; return -6; } // 設定渲染的目標區域 SDL_Rect destRect; destRect.x = 0; destRect.y = 0; destRect.w = w; destRect.h = h; // 7. 復制材質到渲染器對象 if (SDL_RenderCopy(render, texture, NULL, &destRect)) { cout << SDL_GetError() << endl; return -7; } // 8. 執行渲染操作 SDL_RenderPresent(render); getchar(); return 0; }
執行:
可以看到我們創建的800*600的紅色圖像已經成功的被渲染到窗口當中。
三. 模擬動態修改圖像數據
這里模塊通過SDL渲染多幅畫面,使畫面發生變化。
修改上述code:
#include <iostream> #include <sdl/SDL.h> using namespace std; #pragma comment(lib, "SDL2.lib") // SDL 庫里面定義了一個宏 main, 這里給取消掉,否則會與main 函數名沖突,導致編譯錯誤 #undef main int main() { // 定義圖像的寬高 int w = 800; int h = 600; // 1. 初始化SDL庫, 成功返回0, 失敗返回非0值 if (SDL_Init(SDL_INIT_VIDEO)) { cout << SDL_GetError() << endl; return -1; } // 2. 創建SDL窗口 auto screen = SDL_CreateWindow("test_sdl_ffmpeg", // 窗口標題 SDL_WINDOWPOS_CENTERED, // 窗口位置 SDL_WINDOWPOS_CENTERED, w, h, // 窗口寬高 SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE // 窗口屬性,指定使用OpenGL, 並且可調整大小 ); if (!screen) { cout << SDL_GetError() << endl; return -2; } // 3. 創建渲染器 auto render = SDL_CreateRenderer(screen, // 指定渲染到哪個窗口 -1, // 指定渲染器驅動,默認傳-1 SDL_RENDERER_ACCELERATED // 指定渲染模式,這里采用硬件加速模式 ); if (!render) { cout << SDL_GetError() << endl; return -3; } // 4. 在渲染器當中創建一個材質 auto texture = SDL_CreateTexture(render, // 指定在哪個渲染器當中創建 SDL_PIXELFORMAT_ARGB8888, // 指定當前材質的像素格式 SDL_TEXTUREACCESS_STREAMING, // 設定當前材質可修改 w, h // 指定材質寬高 ); if (!texture) { cout << SDL_GetError() << endl; return -4; } // 准備一幅800*600的紅色RGB圖像數據 shared_ptr<unsigned char> rgb(new unsigned char[w * h * 4]); // 乘以4是因為像素格式已指定為ARGB888,單個像素點占4字節 auto r = rgb.get(); unsigned char tmp = 255; for (;;) { SDL_Event ev; SDL_WaitEventTimeout(&ev, 10); if (ev.type == SDL_QUIT) { SDL_DestroyWindow(screen); break; } tmp--; // 為上述圖像數據賦值 for (int j = 0; j < h; j++) { int lineR = j * w * 4; // 每一行R分量的起始位置 for (int i = 0; i < w * 4; i += 4) { r[lineR + i] = 0; // B r[lineR + i + 1] = 0; // G r[lineR + i + 2] = tmp; // R r[lineR + i + 3] = 0; // A } } // 5. 將內存中的RGB數據寫入材質 if (SDL_UpdateTexture(texture, NULL, r, w * 4)) { cout << SDL_GetError() << endl; return -5; } // 6. 清理渲染區(清理屏幕) if (SDL_RenderClear(render)) { cout << SDL_GetError() << endl; return -6; } // 設定渲染的目標區域 SDL_Rect destRect; destRect.x = 0; destRect.y = 0; destRect.w = w; destRect.h = h; // 7. 復制材質到渲染器對象 if (SDL_RenderCopy(render, texture, NULL, &destRect)) { cout << SDL_GetError() << endl; return -7; } // 8. 執行渲染操作 SDL_RenderPresent(render); } getchar(); return 0; }
添加一個等待10ms的SDL事件,觀察效果:
<完>