代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "sdlqtrgb.h"
#include <sdl/SDL.h>
#include <iostream>

#pragma comment(lib, "SDL2.lib")

static int sdl_width = 0;
static int sdl_height = 0;
static SDL_Window* sdl_win = nullptr;
static SDL_Renderer* sdl_render = nullptr;
static SDL_Texture* sdl_texture = nullptr;
static unsigned char* rgb = nullptr;
static int pix_size = 4;


SdlQtRgb::SdlQtRgb(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);

// 获取视频的长宽
sdl_width = ui.label->width();
sdl_height = ui.label->height();

// 初始化SDL
// =====(一)=====
SDL_Init(SDL_INIT_VIDEO);

// 使用Qt的QLabel来初始化窗口
sdl_win = SDL_CreateWindowFrom((void *)ui.label->winId());

// 使用窗口来创建渲染器
// =====(二)=====
sdl_render = SDL_CreateRenderer(sdl_win, -1, SDL_RENDERER_ACCELERATED);

// 创建材质
// =====(三)=====
sdl_texture = SDL_CreateTexture(sdl_render,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
sdl_width,
sdl_height
);

// 创建RGBA图像所需要的像素空间
rgb = new unsigned char[sdl_width * sdl_height * pix_size];

// 触发timerEvent()槽函数,每30ms执行一次
// (也可以使用多线程来执行)
startTimer(30);
}

void SdlQtRgb::timerEvent(QTimerEvent *ev)
{
static unsigned char tmp = 255;
tmp--;

// =====(四)=====
for (int j = 0; j < sdl_height; j++)
{
int b = j * sdl_width * pix_size;
for (int i = 0; i < sdl_width * pix_size; i += pix_size)
{
rgb[b + i] = 0; // B
rgb[b + i + 1] = tmp; // G
rgb[b + i + 2] = 0; // R
rgb[b + i + 3] = tmp; // A
}
}

// =====(五)=====
SDL_UpdateTexture(sdl_texture, NULL, rgb, sdl_width * pix_size);
SDL_RenderClear(sdl_render);

SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = sdl_width;
rect.h = sdl_height;

// 更新材质
// =====(六)=====
SDL_RenderCopy(sdl_render, sdl_texture, NULL, &rect);
SDL_RenderPresent(sdl_render);
}

SdlQtRgb::~SdlQtRgb()
{
// 清理和释放SDL所使用的资源
SDL_Quit();
}

(一)

SDL_Init(SDL_INIT_VIDEO);

SDL_Init 是 SDL库中的一个函数,主要初始化SDL的视频子系统。

除了SDL_INIT_VIDEO 之外,SDL_Init() 函数还可以接受其他多个参数

SDL_INIT_TIMER:
初始化定时器子系统。如果你的应用需要使用 SDL 的定时功能,这个标志是必要的。

SDL_INIT_AUDIO:
初始化音频子系统。如果你的应用需要播放音频或处理声音数据,应该使用这个标志。

SDL_INIT_VIDEO:
初始化视频子系统,这是创建窗口和渲染图形所必须的。

SDL_INIT_EVERYTHING:
初始化上述所有子系统。这是一个方便的标志,如果你希望一开始就初始化 SDL 的所有功能,可以使用它。

如果想同时初始化视频和音频子系统,可以这样写:

1
2
3
4
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) 
{
// 处理初始化失败的情况
}

这行代码会同时初始化视频和音频子系统,使应用可以进行图形显示和音频播放。

(二)

sdl_render = SDL_CreateRenderer(sdl_win, -1, SDL_RENDERER_ACCELERATED);

SDL_CreateRenderer 是 SDL库中的一个函数,用于创建一个与窗口关联的渲染器。渲染器可以用于在窗口上绘制图形,如纹理、形状等。

函数源码:

1
2
3
SDL_Renderer* SDL_CreateRenderer(SDL_Window* window,
int index,
Uint32 flags);

在这个示例中:

  1. sdl_win:
    • 这是一个已经创建好的窗口,sdl_render 将会与该窗口关联。
  2. -1:
    • 让 SDL 自动选择一个合适的渲染驱动程序。
  3. SDL_RENDERER_ACCELERATED:
    • 指示 SDL 使用硬件加速渲染(通常是通过 GPU)。这种方式比软件渲染(CPU)更高效,尤其是在绘制大量图形时。

参数解释

  1. SDL_Window* window:
    • 这是一个指向 SDL_Window 对象的指针。这个窗口是渲染器将要绘制内容的目标窗口。例子中,sdl_win 是之前通过 SDL_CreateWindow 创建的窗口。
  2. int index:
    • 这个参数指定要使用的渲染驱动程序的索引。如果设置为 -1,SDL 会自动选择第一个支持 flags 中指定特性的可用渲染驱动程序。通常可以将其设置为 -1 以使用默认的渲染驱动程序。
  3. Uint32 flags:
    • 这是一个位掩码,用于指定渲染器的行为。常用的标志有:
      • SDL_RENDERER_ACCELERATED: 使用硬件加速渲染。如果可用,SDL 将使用 GPU 进行渲染,这通常会提高渲染的效率和性能。
      • SDL_RENDERER_SOFTWARE: 使用软件渲染。如果硬件加速不可用或者你明确需要软件渲染,可以使用这个标志。
      • SDL_RENDERER_PRESENTVSYNC: 使渲染器与显示器的垂直同步(VSync)保持一致,防止屏幕撕裂。

(三)

1
2
3
4
5
6
sdl_texture = SDL_CreateTexture(sdl_render,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
sdl_width,
sdl_height
);

SDL_CreateTexture是SDL库中一个函数,用于创建一个纹理,这个纹理可以被渲染器使用到屏幕上进行绘制图像。

函数源码:

1
2
3
4
5
SDL_Texture* SDL_CreateTexture(SDL_Renderer* renderer,
Uint32 format,
int access,
int w,
int h);

参数解释

  1. SDL_Renderer* renderer:
    • 这是一个指向 SDL_Renderer 对象的指针,表示纹理将要关联的渲染器。在你的例子中,sdl_render 是通过 SDL_CreateRenderer 创建的渲染器。
  2. Uint32 format:
    • 这是一个像素格式的标志,用于指定纹理中的像素数据的格式。常见的像素格式包括:
      • SDL_PIXELFORMAT_ARGB8888: 32 位像素格式,其中每个像素包含 8 位 alpha 通道、8 位红色、8 位绿色和 8 位蓝色。这种格式常用于高质量图像,支持透明度。
    • 像素格式决定了纹理的颜色深度和排列方式。
  3. int access:
    • 这是一个标志,指定纹理的访问方式。常用的访问模式有:
      • SDL_TEXTUREACCESS_STATIC: 纹理数据只会被设置一次,不会频繁更新。这种模式下纹理的性能最好。
      • SDL_TEXTUREACCESS_STREAMING: 纹理数据可能会被频繁更改(例如每帧都更新)。这种模式适用于动态生成或更新纹理内容的场景。
      • SDL_TEXTUREACCESS_TARGET: 允许将纹理作为渲染目标,即你可以渲染到这个纹理上,而不是直接渲染到屏幕上。
  4. int w:
    • 纹理的宽度(像素单位)。在你的例子中,这个值是 sdl_width,代表纹理的宽度。
  5. int h:
    • 纹理的高度(像素单位)。在你的例子中,这个值是 sdl_height,代表纹理的高度。

(四)

将一维数组视作二维数据进行赋值

width * height * pix_size:以BGRA的数据为一组,一共有width * height这么多组数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
rgb = new unsigned char[sdl_width * sdl_height * pix_size];

// 遍历高度height
for (int j = 0; j < sdl_height; j++)
{
// 计算当前在一维数据中的位置
int b = j * sdl_width * pix_size;
// 给当前宽度赋值
for (int i = 0; i < sdl_width * pix_size; i += pix_size)
{
rgb[b + i] = 0; // B
rgb[b + i + 1] = tmp; // G
rgb[b + i + 2] = 0; // R
rgb[b + i + 3] = tmp; // A
}
}
84ca162702e8394616ec33e13a65047

(五)

SDL_UpdateTexture(sdl_texture, NULL, rgb, sdl_width * pix_size);

SDL_UpdateTexture 是 SDL 库中的一个函数,用于更新纹理(SDL_Texture)中的像素数据。这个函数通常用于将内存中的图像数据传递到 SDL 纹理中,以便之后可以使用渲染器将纹理绘制到屏幕上。

1
2
3
4
int SDL_UpdateTexture(SDL_Texture* texture,
const SDL_Rect* rect,
const void* pixels,
int pitch);

参数解释

  1. SDL_Texture* texture:
    • 这是一个指向 SDL_Texture 对象的指针,表示要更新的纹理。例子中,sdl_texture 是通过 SDL_CreateTexture 创建的纹理。
  2. const SDL_Rect* rect:
    • 这是一个指向 SDL_Rect 结构体的指针,表示要更新的纹理区域。如果传递 NULL,则表示更新整个纹理。
    • SDL_Rect 结构体包含了 x, y, w, h 四个成员,分别表示更新区域的左上角坐标和宽高。
  3. const void* pixels:
    • 这是一个指向原始像素数据的指针。这个数据通常是一个内存缓冲区,包含要上传到纹理的图像数据。例子中,rgb 是一个指向包含图像数据的内存缓冲区的指针。
  4. int pitch:
    • pitch 表示每行像素数据的字节数,通常是 (图像宽度 * 每个像素的字节数)。在你的例子中,这个值是 sdl_width * pix_size
    • 这个参数告诉 SDL 在内存中如何布局图像数据,特别是每行的起始位置。

纹理更新的用途

  • 当你有一个动态变化的图像或视频流时,你可以使用 SDL_UpdateTexture 来更新纹理中的像素数据,以反映这些变化。
  • 在更新纹理数据后,你可以使用 SDL_RenderCopySDL_RenderCopyEx 将纹理绘制到屏幕上。

注意事项

  • SDL_UpdateTexture 会将数据从 CPU 内存复制到 GPU 内存,因此频繁调用可能会影响性能。对于静态图像数据,建议在初始化时更新一次纹理,而对于动态数据,尽量减少不必要的更新次数。
  • 如果你只更新纹理的一部分,可以传递一个非 NULLSDL_Rect 以指定更新区域,从而提高效率。

(六)

1
SDL_RenderCopy(sdl_render, sdl_texture, NULL, &rect);

函数原型:

1
2
3
4
int SDL_RenderCopy(SDL_Renderer* renderer,
SDL_Texture* texture,
const SDL_Rect* srcrect,
const SDL_Rect* dstrect);

参数解释

  1. SDL_Renderer* renderer:
    • 这是一个指向 SDL_Renderer 对象的指针,表示渲染操作的目标。代码中,sdl_render 是之前通过 SDL_CreateRenderer 创建的渲染器。
  2. SDL_Texture* texture:
    • 这是一个指向 SDL_Texture 对象的指针,表示要渲染的纹理。代码中,sdl_texture 是之前创建并可能通过 SDL_UpdateTexture 更新过的纹理。
  3. const SDL_Rect* srcrect:
    • 这是一个指向 SDL_Rect 结构体的指针,表示纹理的源矩形区域。srcrect 定义了从纹理中提取的部分。如果传递 NULL,则表示使用整个纹理。
    • 代码中,传递了 NULL,这意味着将整个纹理内容复制到渲染目标。
  4. const SDL_Rect* dstrect:
    • 这是一个指向 SDL_Rect 结构体的指针,表示渲染器目标(窗口)上的目标矩形区域。纹理将被缩放并绘制到这个矩形区域中。
    • 代码中,&rect 是一个指向 SDL_Rect 结构体的指针,定义了在窗口中绘制纹理的区域大小和位置。

SDL_RenderCopy 的作用是将纹理 sdl_texture 中的图像数据复制到 sdl_render 渲染器的目标区域。目标区域由 &rect 定义。这个操作会将纹理的数据按照 rect 的定义(包括位置和大小)复制到渲染器的后台缓冲区中,准备在下一帧中显示。


1
SDL_RenderPresent(sdl_render);

函数解释

  • SDL_RenderPresent 是 SDL 渲染过程的最后一步,它将渲染器的后台缓冲区内容呈现到屏幕上。
  • 在使用 SDL_Renderer 进行渲染时,所有的绘制操作(如 SDL_RenderCopy)都是在后台缓冲区中完成的。后台缓冲区是一块内存区域,用于暂存当前帧的图像内容,以避免直接在屏幕上进行逐步绘制,防止闪烁或不完整的显示效果。