zoukankan      html  css  js  c++  java
  • 音视频技术应用(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事件,观察效果:

    <完>

  • 相关阅读:
    阿里云短信服务工具类
    vue.config.js
    elementui Tree 树形控件增删改查
    vue 实时显示年月日时分秒星期上下午
    1553:【例 2】暗的连锁
    CF825G Tree Queries
    最短母串
    寻找好串
    无限链计数
    异或运算
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/15554265.html
Copyright © 2011-2022 走看看