SDL2视频渲染

文章目录

  • 前言
  • 一、Qt渲染
  • 二、SDL2渲染
    • [2.1 SDL2安装(linux qt)](#2.1 SDL2安装(linux qt))
    • [2.2 SDL2的简单使用](#2.2 SDL2的简单使用)
      • [2.2.1 初始化 SDL 库SDL_Init](#2.2.1 初始化 SDL 库SDL_Init)
      • [2.2.2 创建窗口 SDL_CreateWindow](#2.2.2 创建窗口 SDL_CreateWindow)
      • [2.2.3 创建渲染器 SDL_CreateRenderer](#2.2.3 创建渲染器 SDL_CreateRenderer)
      • [2.2.4 创建纹理(材质)SDL_CreateTexture](#2.2.4 创建纹理(材质)SDL_CreateTexture)
      • 2.2.5更新纹理SDL_UpdateTexture
      • [2.2.6 渲染操作](#2.2.6 渲染操作)
    • [2.3 SDL渲染Qt控件](#2.3 SDL渲染Qt控件)
  • 结束语

  • 💂 个人主页 :风间琉璃
  • 🤟 版权 : 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主
  • 💬 如果文章对你有帮助欢迎关注点赞收藏(一键三连)订阅专栏

前言

提示:这里可以添加本文要记录的大概内容:


一、Qt渲染

通过 QImage来操作图像的像素,并使用 QPainter 将图像渲染到窗口。

c++ 复制代码
// 重写paintEvent方法,在窗口需要重新绘制时调用
void MainWindow::paintEvent(QPaintEvent *ev)
{
    // 创建一个指定大小(1280x720)且颜色格式为RGB888的QImage对象
    QImage img(w, h, QImage::Format_RGB888);

    // 获取指向图像像素数据的指针
    auto d = img.bits();


    // 垂直遍历图像的每一行(每个像素的Y值)
    for (int i = 0; i < h; i++)
    {

        // 计算当前行的起始地址
        int b = i * w * 3;

        // 水平遍历图像的每个像素(每个像素由RGB三个分量组成)
        for (int j = 0; j < w * 3; j+=3)
        {
            d[b + j]    = 255;       // 设置当前像素的红色分量
            d[b + j + 1] = 125;       // 设置当前像素的绿色分量
            d[b + j + 2] = 0;         // 设置当前像素的蓝色分量
        }
    }

    // 创建QPainter对象,用于在窗口上绘制
    QPainter p;
    p.begin(this);             // 使painter准备绘制到当前窗口
    p.drawImage(0, 0, img);    // 在窗口的(0, 0)位置绘制图像img
    p.end();

}

paintEvent 中,创建了一个 1280x720 的 QImage对象,并使用 QImage::Format_RGB888格式来表示每个像素的红、绿、蓝分量(每个分量用8位表示)。然后逐行填充图像数据,每行的每个像数值的RGB可以赋值为不同的值,就可以显示不同的颜色。最后使用 QPainter 来绘制生成的图像。

二、SDL2渲染

SDL2(Simple DirectMedia Layer 2)是一个跨平台的多媒体开发库,主要用于图形渲染、音频播放、键盘和鼠标输入、游戏控制器支持以及文件 I/O 操作等。它为开发者提供了一个高效的接口,使得音视频、图像处理等任务变得更加简单。SDL2 是为游戏开发和多媒体应用设计的,因此它的功能集中于低层次的硬件控制和图形渲染。

2.1 SDL2安装(linux qt)

①Linux下安装,通过 APT 包管理器安装 SDL2 库, 这会安装 SDL2 库的开发版本,包括头文件和静态库/动态库。也可以通过源码的方法进行安装,参考如下。

bash 复制代码
sudo apt update
sudo apt upgrade
sudo apt install libsdl2-dev

参考:SDL2安装

在Qt中配置SDL2,如下所示:

bash 复制代码
INCLUDEPATH += /usr/include/SDL2
LIBS +=  -L/usr/lib/x86_64-linux-gnu -lSDL2

添加 INCLUDEPATH 以指明 SDL2 头文件的路径(一般在/usr/include/SDL2)和添加 LIBS 以指明 SDL2 库的路径。

库路径查找

bash 复制代码
find /usr/lib -name "libSDL2*"

很奇怪这样第一次编译之后可以正常运行,后面编译直接报上面的错误,然后去掉SDL2库链接,程序又可以运行,大概率是库冲突问题,没办法,只能试试源码安装,可以指定库目录。下载链接:SDL2

②下载源码后进行主目录,执行如下指令

bash 复制代码
mkdir build
cd build
../configure
make -j32
sudo make install

验证是否安装成功:

最后安装加了sudo,一般库文件和头文件在/usr/local/lib、 /usr/local/include。

在Qt配置如下

bash 复制代码
# 添加 SDL2 的头文件路径
INCLUDEPATH += /usr/local/include/SDL2

# 添加 SDL2 的库路径
LIBS += -L/usr/local/lib  -lSDL2

最终测试没问题的!测试如下。

2.2 SDL2的简单使用

在SDL中头文件是SDL2/SDL.h,它定义了 SDL2 的许多函数、数据类型和常量。

2.2.1 初始化 SDL 库SDL_Init

在使用 SDL 之前,需要初始化 SDL 的相关子系统。最常用的子系统是 SDL_INIT_VIDEO 用于视频和窗口操作。可以选择初始化多个子系统,如视频、音频、事件等。

  • 函数原型int SDL_Init(Uint32 flags);

  • 参数说明flags:指定要初始化的 SDL 子系统。这个参数是一个按位与的标志位,表示你要启用的子系统,可以是以下几种组合,通过按位"或" (|) 操作符组合多个标志来初始化多个子系统

    • SDL_INIT_VIDEO:初始化视频子系统(用于显示图形)。

    • SDL_INIT_AUDIO:初始化音频子系统(用于播放声音)。

    • SDL_INIT_EVENTS:初始化事件子系统(用于处理用户输入)。

    • SDL_INIT_TIMER:初始化定时器子系统

  • 返回值:

    • 返回 0 表示初始化成功。
    • 如果初始化失败,返回负值,并可以通过 SDL_GetError() 获取错误信息。
c++ 复制代码
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) {
        SDL_Log("SDL initialization failed: %s", SDL_GetError());
        return -1;
    }

2.2.2 创建窗口 SDL_CreateWindow

(1)SDL_CreateWindow 是 SDL 库中用于创建一个窗口的函数,窗口可以用来显示图形、处理事件以及与用户交互。SDL 提供了跨平台的窗口创建接口,支持各种操作系统,如 Windows、Linux 和 macOS。

c++ 复制代码
SDL_Window* SDL_CreateWindow(const char* title,
                             int x, int y,
                             int w, int h,
                             Uint32 flags);

参数:

  • title:窗口的标题栏文字 ,可以设置为 NULL,此时 SDL 会使用默认标题。
  • x和 y:窗口的初始位置 ,分别为窗口的左上角在屏幕上的 X 和 Y 坐标。
    • SDL_WINDOWPOS_UNDEFINED,表示窗口的位置由操作系统决定。
    • SDL_WINDOWPOS_CENTERED:将窗口在屏幕上居中显示。
  • w 和 h:窗口的宽度和高度,即窗口的大小,单位是像素。
  • flags:窗口的标志,可以组合多个标志位,常见的有:
    • SDL_WINDOW_SHOWN:创建一个可见的窗口。
    • SDL_WINDOW_HIDDEN:创建一个初始时隐藏的窗口。
    • SDL_WINDOW_BORDERLESS:无边框窗口。
    • SDL_WINDOW_RESIZABLE:可调整大小的窗口。
    • SDL_WINDOW_MAXIMIZED/SDL_WINDOW_MINIMIZED:最大化窗口/最小化窗口。
    • SDL_WINDOW_FULLSCREEN:全屏窗口。
    • SDL_WINDOW_OPENGL:窗口支持 OpenGL 渲染(需要 OpenGL 环境)。
    • SDL_WINDOW_VULKAN:窗口支持 Vulkan 渲染(需要 Vulkan 环境)。

返回值:

  • 成功时,返回一个指向 SDL_Window 结构体的指针,即创建的窗口对象。
  • 失败时,返回 NULL,并可以通过 SDL_GetError() 获取错误信息。
c++ 复制代码
    // 创建一个标题为 "SDL Window Example" 的窗口,位置居中,大小为800x600
    SDL_Window* window = SDL_CreateWindow("SDL Window Example",
                                          SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
                                          800, 600, SDL_WINDOW_SHOWN);
    if (!window) {
        SDL_Log("Window creation failed: %s", SDL_GetError());
        SDL_Quit();
        return -1;
    }

注意,当窗口不再需要时,应当调用 SDL_DestroyWindow 来销毁它,避免内存泄漏。

(2)SDL_CreateWindowFrom 是 SDL 中的一个函数,允许通过传递一个现有的窗口句柄(通常是外部创建的窗口)来创建 SDL 窗口。一般都是将Qt创建的ui控件作为窗口传入。

c++ 复制代码
SDL_Window * SDLCALL SDL_CreateWindowFrom(const void *data);

data:一个 void 类型的指针*,通常是一个窗口句柄或结构体指针,表示一个现有的窗口。

SDL_CreateWindowFrom主要用于与外部窗口系统的集成,比如qt, 使用时需要确保传递的 data 是有效且适配 SDL 的窗口数据结构。

2.2.3 创建渲染器 SDL_CreateRenderer

SDL_CreateRenderer 是 SDL 库中用于创建渲染器(SDL_Renderer)的函数 。渲染器是一个重要的对象,它负责将图形绘制到窗口中。通过渲染器,可以进行各种图形绘制操作,如填充矩形、绘制图像、渲染文本等。渲染器可以使用不同的硬件加速方法,如 OpenGL 或软件渲染。

c++ 复制代码
SDL_Renderer* SDL_CreateRenderer(SDL_Window* window, int index, Uint32 flags);
  • window:需要渲染的SDL目标窗口,渲染器会被绑定到这个窗口。

  • index:指定渲染驱动的索引。通常为 -1 来选择默认的渲染驱动

  • flags:渲染器的标志,影响渲染器的工作模式,可以是以下几种组合:

    • SDL_RENDERER_ACCELERATED:请求硬件加速渲染 ,如果硬件支持加速,则使用硬件渲染,否则回退到软件渲染

    • SDL_RENDERER_SOFTWARE:请求使用软件渲染。适用于不支持硬件加速的系统。

    • SDL_RENDERER_PRESENTVSYNC:启用垂直同步(V-Sync)。当设置此标志时,渲染器会等待显示器的刷新周期来更新图像,防止画面撕裂。

    • SDL_RENDERER_TARGETTEXTURE:允许渲染到纹理上。使用该标志时,渲染器可以直接绘制到纹理对象中,适用于渲染到离屏缓冲区的操作。

c++ 复制代码
    // 创建渲染器
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (!renderer) {
        SDL_Log("Renderer creation failed: %s", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return -1;
    }  

渲染器的工作方式:

  • 渲染器负责将你在代码中绘制的图形、图片或其他元素渲染到窗口上。
  • 渲染器可以通过 CPU(软件渲染)或 GPU(硬件加速渲染)进行渲染。如果设备支持硬件加速并且使用了 SDL_RENDERER_ACCELERATED 标志,SDL 会尽量选择硬件加速渲染来提高性能。

2.2.4 创建纹理(材质)SDL_CreateTexture

SDL_CreateTexture 是 SDL 库中的一个函数,用于创建一个纹理对象 。纹理是图形渲染中的一个重要概念,通常用于表示图像或图形的像素数据。在 SDL 中,纹理对象由渲染器管理,并可以用来在窗口中渲染图像、绘制图形等

c++ 复制代码
SDL_Texture* SDL_CreateTexture(SDL_Renderer* renderer,
                               Uint32 format, int access, int w, int h);
  • renderer:需要与之关联的渲染器 对象。渲染器负责管理和渲染纹理

  • format:指定纹理的像素格式,通常是使用 SDL_PIXELFORMAT_* 常量来指定。

    • SDL_PIXELFORMAT_RGB24:表示每个像素使用 24 位,分别存储 RGB 信息。

    • SDL_PIXELFORMAT_RGBA32:表示每个像素使用 32 位,存储 RGBA 信息(包括透明度)。

    • SDL_PIXELFORMAT_YV12:用于视频纹理(YUV 格式)。

    • SDL_PIXELFORMAT_ARGB8888:表示每个像素使用 32 位,存储 ARGB信息(包括透明度)。

  • access:指定纹理的访问方式,影响纹理的读取和写入方式:

    • SDL_TEXTUREACCESS_STATIC:静态纹理。创建后不再改变,适用于直接加载图像内容。

    • SDL_TEXTUREACCESS_STREAMING流式纹理 。允许在纹理中频繁更新像素数据 ,适用于需要不断更新内容的情况,如视频播放或动态绘制

    • SDL_TEXTUREACCESS_TARGET:目标纹理。表示渲染的目标,可以将图形渲染到纹理上而不是直接显示到屏幕上。

  • w 和 h:指定纹理的宽度和高度(单位为像素)。

c++ 复制代码
	auto texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_ARGB8888,
                      SDL_TEXTUREACCESS_STREAMING, // 流式纹理
                      w, h);  // 设置纹理的宽度和高度
    if (!texture)
    {
        cout << SDL_GetError() << endl;  // 如果创建纹理失败,输出错误信息
    }

还可以用SDL_CreateTextureFromSurface创建纹理,这个函数用于从一个 SDL_Surface 创建一个纹理,SDL_Surface 通常表示一个图像数据。它可以用于从图像文件中创建纹理。例如:

c++ 复制代码
SDL_Texture* SDL_CreateTextureFromSurface(SDL_Renderer* renderer, SDL_Surface* surface);

可以先用 SDL_LoadBMPIMG_Load 加载一个图像到一个 SDL_Surface 中,再将其转换为纹理。

纹理的常见用途:

  1. 显示图片:通过将图像加载到纹理中,可以在渲染器中绘制该纹理,显示图片。
  2. 离屏渲染 :通过创建目标纹理(SDL_TEXTUREACCESS_TARGET)进行离屏渲染,即将渲染结果保存到纹理中,之后再将其显示到屏幕上。
  3. 视频和动画 :动态更新纹理内容(使用 SDL_TEXTUREACCESS_STREAMING)可以用于播放视频或者动态生成的动画效果

2.2.5更新纹理SDL_UpdateTexture

SDL_UpdateTexture 是 SDL 库中用于更新纹理内容的函数。它允许将内存中的像素数据 传输到一个纹理对象中,以便在渲染时使用。通常,这个函数用于动态生成或修改纹理内容,特别是在需要从自定义图像数据(例如视频帧、游戏中的像素数据等)更新纹理时。

c++ 复制代码
int SDL_UpdateTexture(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch);
  • texture:这是要更新的目标纹理对象。SDL_Texture 是一个纹理对象,代表了一块显存中的图像数据。

  • rect:一个 SDL_Rect 结构体,定义了要更新的纹理区域。如果为 NULL,则表示更新整个纹理。

  • x, y: 区域的左上角位置(相对于纹理的坐标系)。

  • w, h: 区域的宽度和高度。

  • pixels:指向要传输到纹理的数据的指针。这个数据通常是一个 void* 类型的指针,指向一个包含像素数据的内存区域。像素数据通常是按行存储的,可以是 RGB、RGBA 等格式的数据。

  • pitch:每一行的字节数 ,通常是纹理宽度(w)乘以每个像素的字节数。如果你不清楚,可以直接传入宽度乘以纹理的像素格式的字节大小(例如,对于 SDL_PIXELFORMAT_ARGB8888,每个像素需要 4 个字节)

c++ 复制代码
// 更新纹理,将内存中的数据写入纹理 r---> texture
SDL_UpdateTexture(texture, NULL, r, w * 4);

2.2.6 渲染操作

(1)SDL_RenderClear

SDL_RenderClear 是 SDL 中用于清空渲染器目标的函数,通常在每一帧的开始调用,以确保屏幕上显示的是当前帧的渲染内容。该函数会使用当前设置的渲染颜色SDL_SetRenderDrawColor()清除渲染器的目标区域(通常是窗口的显示区域)。在调用 SDL_RenderClear 后,所有之前绘制的内容都会被清除,下一步渲染的内容将覆盖之前的内容。如果没有明确设置渲染颜色,SDL 默认使用黑色(0, 0, 0, 255)。

c++ 复制代码
int SDL_RenderClear(SDL_Renderer* renderer); // renderer:需要清空的渲染器

(2)SDL_RenderCopy

SDL_RenderCopy 是 SDL 中用于将纹理绘制到渲染器目标区域的函数。它会从指定的纹理中提取图像数据,并将其复制到渲染目标(通常是屏幕或窗口)。此函数是将纹理显示在窗口中的主要方式之一。

c++ 复制代码
int SDL_RenderCopy(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* srcRect, const SDL_Rect* dstRect);
  • renderer:指向渲染器对象的指针,表示渲染目标(通常是屏幕或窗口)。

  • texture:指向要渲染的纹理对象的指针。该纹理包含图像数据。

  • srcRect:指向源矩形的指针,指定纹理中要渲染的区域 。如果为 NULL,则默认将整个纹理绘制出来。

  • dstRect:指向目标矩形的指针,指定纹理将绘制到渲染目标的哪个位置和大小。如果为 NULL,则默认将纹理绘制到渲染目标的原点,尺寸与纹理的原始尺寸相同。

SDL_RenderCopy用于将图像纹理渲染到屏幕或窗口上,常用于加载和显示图片、图标、背景等。

(3)SDL_RenderPresent

SDL_RenderPresent 是 SDL 中用于将渲染器中的图形内容呈现到窗口显示区域的函数。它的作用是将你在渲染器中绘制的所有图形内容(例如,图像、文本、形状等)实际显示到屏幕上。这个操作通常发生在每一帧的渲染循环的末尾。

c++ 复制代码
void SDL_RenderPresent(SDL_Renderer* renderer);  // renderer:用于渲染操作的渲染器。

SDL_RenderPresent 将渲染器缓冲区中的内容呈现到屏幕上。当调用 SDL_RenderCopy、SDL_RenderDrawRect 等函数绘制图形时,所有的操作都是在渲染器的内部缓冲区中完成的。调用 SDL_RenderPresent 后,渲染器的缓冲区内容被"提交"到窗口中进行显示。

在大多数情况下,渲染是双缓冲的,这意味着 SDL 在后台会有一个"前缓冲"显示到屏幕,而"后缓冲"用来进行绘制。SDL_RenderPresent 会将后缓冲中的内容显示到屏幕,确保每一帧的图像是清晰的,不会显示出不完整的图形或部分渲染的图像。

使用示例:

c++ 复制代码
    // 1. 初始化SDL视频库
    if (SDL_Init(SDL_INIT_VIDEO))
    {
        SDL_Log("SDL Initialization failed: %s", SDL_GetError());
    }

    // 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)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
    }

    // 3. 创建SDL渲染器
    auto render = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED);  // 使用加速渲染
    if (!render)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
    }

    // 4. 创建SDL纹理
    auto texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_ARGB8888,
                                     SDL_TEXTUREACCESS_STREAMING, // 流式纹理
                                     w, h);  // 设置纹理的宽度和高度
    if (!texture)
    {
        SDL_Log("SDL_CreateTexture failed: %s", SDL_GetError());  
    }

    // 存放图像数据的智能指针(RGB格式)
    shared_ptr<unsigned char> rgb(new unsigned char[w * h * 4]);
    auto r = rgb.get();  // 获取原始数据指针
    unsigned char tmp = 255;  // 用于渐变效果的初始值

    for (;;)
    {
        // 处理SDL事件
        SDL_Event ev;
        SDL_WaitEventTimeout(&ev, 10);  // 等待事件10ms
        if (ev.type == SDL_QUIT)  // 如果窗口关闭,退出循环
        {
            SDL_DestroyWindow(screen);  // 销毁窗口
            break;
        }
        
        tmp--;  // 改变渐变值
        // 垂直遍历图像的每一行(每行有w个像素)
        for (int j = 0; j < h; j++)
        {
            int b = j * w * 4;  // 计算每一行数据在内存中的起始位置
            for (int i = 0; i < w * 4; i += 4)  // 水平遍历图像的每个像素(每个像素由4个分量:RGBA)
            {
                r[b + i] = 0;           // 设置蓝色分量为0
                r[b + i + 1] = 0;       // 设置绿色分量为0
                r[b + i + 2] = tmp;     // 设置红色分量为渐变值
                r[b + i + 3] = 0;       // 设置透明度为0(完全透明)
            }
        }

        // 5. 更新纹理,将内存中的数据写入纹理 r---> texture
        SDL_UpdateTexture(texture, NULL, r, w * 4);

        // 6. 清空屏幕
        SDL_RenderClear(render);

        SDL_Rect sdl_rect;  // 定义渲染矩形
        sdl_rect.x = 0;     // 设置矩形的X坐标
        sdl_rect.y = 0;     // 设置矩形的Y坐标
        sdl_rect.w = w;     // 设置矩形的宽度
        sdl_rect.h = h;     // 设置矩形的高度

        // 7. 将纹理复制到渲染器
        SDL_RenderCopy(render, texture,
                       NULL,       // 原始纹理区域
                       &sdl_rect); // 目标渲染区域

        // 8. 显示渲染内容
        SDL_RenderPresent(render);
    }

常用函数和作用总结

函数 作用
SDL_Init 初始化 SDL 子系统
SDL_CreateWindow 创建一个窗口
SDL_CreateWindowFrom 传递现有的窗口句柄(qt)来创建 SDL 窗口
SDL_CreateRenderer 创建一个与窗口关联的渲染器
SDL_CreateTexture 纹理表示图像或图形的像素数据
SDL_UpdateTexture 将内存中的像素数据传输到一个纹理对象中
SDL_RenderClear 清除渲染器的内容
SDL_RenderCopy 将纹理绘制到渲染器目标区域:屏幕或窗口
SDL_RenderPresent 更新渲染器内容并显示到屏幕
SDL_SetRenderDrawColor 设置渲染器的绘图颜色
SDL_PollEvent/SDL_WaitEvent 非阻塞地获取事件/阻塞等待事件
SDL_DestroyRenderer/SDL_DestroyWindow 销毁渲染器和窗口
SDL_Quit 关闭 SDL 子系统并释放它所占用的资源

2.3 SDL渲染Qt控件

使用 startTimer(10)设置定时器,以便每隔10毫秒调用一次 timerEvent函数来更新显示内容,实现动态的渐变效果。示例如下:

c++ 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPainter>
#include <QImage>
#include <SDL2/SDL.h>
#include <memory>

static SDL_Window* sdl_window = NULL;        // SDL 窗口对象
static SDL_Renderer* sdl_renderer = NULL;    // SDL 渲染器对象
static SDL_Texture* sdl_texture = NULL;      // SDL 纹理对象
static int sdl_width = 0;                     // SDL 窗口的宽度
static int sdl_height = 0;                    // SDL 窗口的高度
static unsigned char* rgb_data = NULL;        // 存储图像数据的缓冲区
static int pixel_size = 4;                    // 每个像素所占字节数(RGBA格式,4字节)

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 获取label控件的宽度和高度,作为SDL窗口的尺寸
    sdl_width = ui->label->width();
    sdl_height = ui->label->height();

    // 1. 初始化 SDL
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        SDL_Log("SDL_Init failed: %s", SDL_GetError());
        return;
    }

    // 2. 创建一个SDL窗口,并将其与QWidget的窗口关联
    sdl_window = SDL_CreateWindowFrom((void*)ui->label->winId());
    if (!sdl_window) {
        SDL_Log("SDL_CreateWindowFrom failed: %s", SDL_GetError());
        SDL_Quit();
        return;
    }

    // 3. 创建一个渲染器,并为其启用加速
    sdl_renderer = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_ACCELERATED);
    if (!sdl_renderer) {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        SDL_DestroyWindow(sdl_window);
        SDL_Quit();
        return;
    }

    // 4. 创建一个纹理对象
    sdl_texture = SDL_CreateTexture(sdl_renderer,
                                     SDL_PIXELFORMAT_ARGB8888,  // 使用 ARGB 格式
                                     SDL_TEXTUREACCESS_STREAMING, // 支持直接更新纹理数据
                                     sdl_width, sdl_height);      // 设置纹理的宽度和高度
    if (!sdl_texture) {
        SDL_Log("SDL_CreateTexture failed: %s", SDL_GetError());
        SDL_DestroyRenderer(sdl_renderer);
        SDL_DestroyWindow(sdl_window);
        SDL_Quit();
        return;
    }

    // 5. 分配内存空间用于存储图像数据(RGBA格式)
    rgb_data = new unsigned char[sdl_width * sdl_height * pixel_size];

    // 6. 启动定时器,定时调用 timerEvent 函数(10ms一次)
    startTimer(10);
}

// 定时器事件处理函数
void MainWindow::timerEvent(QTimerEvent* ev)
{
    static unsigned char tmp = 255;  // 用于创建渐变效果的临时变量
    tmp--;      // 逐渐减少值,产生色彩渐变的效果

    // 逐行遍历图像数据
    for (int j = 0; j < sdl_height; j++) {
        int base_index = j * sdl_width * pixel_size;  // 每行的起始索引
        for (int i = 0; i < sdl_width * pixel_size; i += pixel_size) {
            rgb_data[base_index + i] = 0;          // 蓝色分量
            rgb_data[base_index + i + 1] = tmp;    // 绿色分量(随着tmp变化)
            rgb_data[base_index + i + 2] = 0;      // 红色分量
            rgb_data[base_index + i + 3] = 0;      // 透明度分量(完全透明)
        }
    }

    // 7. 更新纹理,上传修改后的图像数据
    SDL_UpdateTexture(sdl_texture, NULL, rgb_data, sdl_width * pixel_size);

    // 8. 清空渲染器的屏幕内容
    SDL_RenderClear(sdl_renderer);

    // 9. 设置渲染目标区域
    SDL_Rect render_rect;
    render_rect.x = 0;  // 左上角X坐标
    render_rect.y = 0;  // 左上角Y坐标
    render_rect.w = sdl_width;  // 渲染区域宽度
    render_rect.h = sdl_height; // 渲染区域高度

    // 10. 将纹理绘制到渲染器
    SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, &render_rect);

    // 11. 更新屏幕,显示渲染结果
    SDL_RenderPresent(sdl_renderer);
}

MainWindow::~MainWindow()
{
    delete ui;
    // 释放SDL资源
    delete[] rgb_data;  // 删除图像数据缓冲区
    SDL_DestroyTexture(sdl_texture);  // 销毁纹理
    SDL_DestroyRenderer(sdl_renderer);  // 销毁渲染器
    SDL_DestroyWindow(sdl_window);  // 销毁窗口
    SDL_Quit();  // 退出SDL
}

结束语

感谢阅读吾之文章,今已至此次旅程之终站 🛬。

吾望斯文献能供尔以宝贵之信息与知识也 🎉。

学习者之途,若藏于天际之星辰🍥,吾等皆当努力熠熠生辉,持续前行。

然而,如若斯文献有益于尔,何不以三连为礼?点赞、留言、收藏 - 此等皆以证尔对作者之支持与鼓励也 💞。

相关推荐
Highcharts.js2 小时前
为什么要创建音频地图?——探索Highcharts可视化的声音创新
信息可视化·音视频
FFZero12 小时前
积加科技音视频一面
c++·科技·音视频
开开心心就好2 小时前
专业视频修复软件,简单操作效果好
音视频
CChenhire2 小时前
网站多媒体加载卡顿?视频压缩 + 音频优化,加载速度提升 75% 的实操方法
音视频
痕忆丶2 小时前
vlc播放NV12原始视频数据
音视频
Black蜡笔小新2 小时前
视频汇聚平台EasyCVR如何构建智慧农业监控监管系统?
音视频
声光界3 小时前
《LE Audio & Auracast开创你我新生活》
音视频·声学·音频技术·蓝牙音频
奕星星奕4 小时前
Linux编译SRS并测试RTMP流
音视频
向阳花开_miemie5 小时前
Android音频学习(二十)——高通HAL
android·学习·音视频