音視頻技術應用(7)-使用SDL渲染一幅指定的圖像,並且動態修改圖像數據


一. 基本步驟

使用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, 代表的是窗口的坐標, 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事件,觀察效果:

 

<完>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM