个人技术分享

cpp文件:

#include "sdlqtrgb.h"
#include <sdl/SDL.h>
static SDL_Window* sdl_win = NULL;
static SDL_Renderer* sdl_render = NULL;
static SDL_Texture* sdl_texture = NULL;
static int sdl_width = 0;
static int sdl_height = 0;
static unsigned  char* rgb = NULL;
static int pix_size = 4;
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] = 0;     //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(QWidget* parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    sdl_width = ui.label->width();
    sdl_height = ui.label->height();
    //初始化SDL
    SDL_Init(SDL_INIT_VIDEO);
    //创建窗口
    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
    );
    rgb = new unsigned char[sdl_width * sdl_height * pix_size];
    startTimer(10);
}

1. 前要知识

1.像素

在这个上下文中,像素通常被表示为一组连续的颜色分量值。对于RGB图像来说,每个像素通常由三个分量组成:红色(R)、绿色(G)和蓝色(B)。在某些情况下还可能包含一个Alpha通道(A),像素的颜色分量值以一定的顺序存储在内存中,用一个一维数组存储,你可以想象成BGRABGRABGRABGRA........,一组BGRA就是一个像素点,不同分量的值的配比对应不同的颜色

2.代码中的循环

在这个循环中,每次迭代的单位是像素。换句话说,每次循环迭代处理的是一行中的一个像素。在RGB图像中,一个像素通常由多个字节组成,每个字节对应一个颜色通道(红色、绿色、蓝色和可能的Alpha通道),我们可以想象一张图片有很多行,每一行上有许多像素点(BGRA),比如

BGRABGRABGRABGRABGRA
BGRABGRABGRABGRABGRA
BGRABGRABGRABGRABGRA
BGRABGRABGRABGRABGRA
BGRABGRABGRABGRABGRA
上面是五行 每一行有五个像素点 

2.代码解析

1.定时器函数解析

1.外部循环(行循环):

  • 目的:遍历图像的每一行。
  • 变量:
    • j:当前行的索引,从0开始直到 sdl_height - 1
  • 每次迭代:
    • 计算当前行在图像数据数组中的起始偏移量 bb = j * sdl_width * pix_size
    • 这确保我们处理每一行的像素数据。

2.内部循环(像素循环):

  • 目的:遍历当前行的每个像素,并设置其RGB值。
  • 变量:
    • i:当前像素中第一个颜色通道(通常是蓝色)的偏移量,从0开始,每次递增 pix_size
  • 每次迭代:
    • 对当前像素的每个颜色通道(B、G、R、A),设置相应的值:
      • rgb[b + i] = 0:将蓝色分量(B)设为0。
      • rgb[b + i + 1] = tmp:将绿色分量(G)设为 tmp 的值,它在每次定时器事件中递减,导致逐渐减少的绿色。
      • rgb[b + i + 2] = 0:将红色分量(R)设为0。
      • rgb[b + i + 3] = 0:如果存在,将Alpha通道(A)设为0

3.SDL_UpdateTexture(sdl_texture, NULL, rgb, sdl_width * pix_size) 更新纹理数据

  • 这一步是将新的图像数据(存储在 rgb 数组中)更新到SDL纹理中。
  • 在这个例子中,SDL_UpdateTexture 函数用于更新纹理,使其包含了最新的图像数据。
  • 这个函数的参数包括要更新的纹理、更新的矩形区域(如果为NULL,则更新整个纹理)、指向新图像数据的指针,以及新图像数据的行距(通常为图像宽度乘以每个像素的大小)。

4.SDL_RenderClear 清空当前渲染器

  • 这一步是清空当前渲染器的内容,准备开始绘制新的内容。
  • SDL_RenderClear 函数用于清空渲染器,以便在其上绘制新的内容。

5.SDL_RenderCopy(sdl_render, sdl_texture, NULL, &rect);

  • 在这一步中,你首先定义一个矩形区域,然后使用 SDL_RenderCopy 函数将纹理复制到渲染器中。
  • 这个矩形区域定义了纹理将要绘制到屏幕上的位置和大小。
  • SDL_RenderCopy 函数将指定的纹理复制到渲染器中,并根据矩形区域的位置和大小进行调整。

6.SDL_RenderPresent 更新屏幕显示

  • 这一步是将渲染器中的内容更新到屏幕上,使其可见。
  • SDL_RenderPresent 函数用于更新屏幕显示,将渲染器中的内容呈现到屏幕上。

综上所述,这些步骤组成了SDL中绘制图形的基本流程:更新纹理数据、清空渲染器、复制纹理到渲染器中,然后更新屏幕显示。通过这个流程,你可以实现动态更新和显示图形,例如在这个例子中每次定时器事件都会更新屏幕显示,使图像逐渐变化。

2.构造函数解析

  • 调用 SDL_Init 函数来初始化SDL库的视频子系统。这是必要的,因为我们将使用SDL来处理图形。
  • 调用 SDL_CreateWindowFrom 函数来创建一个SDL窗口,并将其与界面上的 label 控件关联起来。这样,我们可以在该控件内部显示图形。
  • 调用 SDL_CreateRenderer 函数来创建一个SDL渲染器,用于在窗口上进行绘制。参数 -1 表示选择第一个可用的渲染器,SDL_RENDERER_ACCELERATED 表示使用硬件加速。
  • 调用 SDL_CreateTexture 函数来创建一个SDL纹理,用于存储图像数据。参数指定了纹理的格式(ARGB8888)、访问方式(STREAMING,表示数据将被频繁更新)、宽度和高度。
  • 动态分配了一个大小为 sdl_width * sdl_height * pix_size 字节的内存块,并将指针存储在 rgb 变量中。这个内存块用于存储图像数据。
  • 调用 startTimer 函数,启动一个定时器,每隔10毫秒触发一次定时器事件。定时器事件在之前的代码中已经被重载,用于更新图像数据并刷新屏幕显示。

构造函数完成了SDL的初始化、窗口、渲染器和纹理的创建,以及图像数据的内存分配和定时器的启动。整个流程为在Qt应用程序中嵌入SDL图形显示提供了基础。

3.运行结果展示

颜色从绿色到黑色再到绿色一直循环,根据循环里的temp变化而变化