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事件,观察效果:

    <完>

  • 相关阅读:
    2018 ACM 网络选拔赛 徐州赛区
    2018 ACM 网络选拔赛 焦作赛区
    2018 ACM 网络选拔赛 沈阳赛区
    poj 2289 网络流 and 二分查找
    poj 2446 二分图最大匹配
    poj 1469 二分图最大匹配
    poj 3249 拓扑排序 and 动态规划
    poj 3687 拓扑排序
    poj 2585 拓扑排序
    poj 1094 拓扑排序
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/15554265.html
Copyright © 2011-2022 走看看