文章目录
- 前言
- 一、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_LoadBMP
或 IMG_Load
加载一个图像到一个 SDL_Surface
中,再将其转换为纹理。
纹理的常见用途:
- 显示图片:通过将图像加载到纹理中,可以在渲染器中绘制该纹理,显示图片。
- 离屏渲染 :通过创建目标纹理(
SDL_TEXTUREACCESS_TARGET
)进行离屏渲染,即将渲染结果保存到纹理中,之后再将其显示到屏幕上。 - 视频和动画 :动态更新纹理内容(使用
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
}
结束语
感谢阅读吾之文章,今已至此次旅程之终站 🛬。
吾望斯文献能供尔以宝贵之信息与知识也 🎉。
学习者之途,若藏于天际之星辰🍥,吾等皆当努力熠熠生辉,持续前行。
然而,如若斯文献有益于尔,何不以三连为礼?点赞、留言、收藏 - 此等皆以证尔对作者之支持与鼓励也 💞。