4.SDL2.0的编程回顾总结并操练


***【在线视频教程】***

好文章,来自【福优学苑@音视频+流媒体】

4.SDL2.0的编程回顾总结并操练

参考课程:“FFmpeg4.3--系列8--SDL2.0小白入门”



SDL2.0知识框架体系


image.png

SDL 视频渲染主要涉及到四个对象:

SDL_Window

SDL_Surface

SDL_Render

SDL_Texture

SDL_Event



SDL 视频渲染主要涉及到相关API:


初始化: 

SDL_Init(): 初始化SDL。 

SDL_CreateWindow(): 创建窗口(Window)。 

SDL_CreateRenderer(): 基于窗口创建渲染器(Render)。 

SDL_CreateTexture(): 创建纹理(Texture)。 


循环渲染数据: 

SDL_UpdateTexture(): 设置纹理的数据。 

SDL_RenderCopy(): 纹理复制给渲染器。 

SDL_RenderPresent(): 显示。


退出: 

SDL_DestroyWindow()

SDL_DestroyRenderer();

SDL_DestroyTexture();

SDL_Quit();




SDL 主要涉及到几个扩展库:

SDL_image

SDL_mixer

SDL_ttf






SDL2.0显示一个跳动的方块


1. SDL 视频渲染相关对象

SDL 视频渲染主要涉及到四个对象:SDL_Window、SDL_Render、SDL_Texture和SDL_Surface。


SDL_Window代表的是窗口的逻辑概念,它是存放在主内存中的一个对象。当我们调用SDL API 创建窗口后,它并不会被显示出来。


SDL_Render 是渲染器,它也是主存中的一个对象。对Render操作时实际上分为两个阶段:


渲染阶段。在该阶段,用户可以画各种图形渲染到SDL_Surface或SDL_Texture 中;

显示阶段。参SDL_Texture为数据,通过OpenGL操作GPU,最终将 SDL_Surfce 或SDL_Texture中的数据输出到显示器上。

SDL_Render对象中有一个视频缓冲区,该缓冲区我们称之为SDL_Surface,它是按照像素存放图像的。我们一般把真彩色的像素称为RGB24数据。也就是说,每一个像素由24位组成,每8位代表一种颜色,像素的最终颜色是由RGB三种颜色混合而成的。


SDL_Texture 与SDL_Surface 相似,也是一种缓冲区。只不过它存放的不是真正的像素数据,而是存放的图像的描述信息。这些描述信息通过OpenGL、D3D 或 Metal等技术操作GPU,从而绘制出与SDL_Surface一样的图形,且效率更高。


2. 使用 SDL_Texute 渲染纹理的步骤

SDL提供了操作SDL_Texture的方法,使用SDL_Texute的基本步骤为:

创建一个 SDL_Texture。

渲染 Texture

Destory Texture

3. 使用SDL渲染纹理的核心API

a). 创建 SDL_Texture

SDL_Texture* SDL_CreateTexture(SDL_Renderer* renderer,  // 渲染器

           Uint32        format,// 渲染数据的格式,如YUV、RGB     

           int           access,// Texture 类型,target、stream 

           int           w,                

           int           h)

下面说明一下重要参数:

format: 指明像素格式,可以是YUV,也可以是RGB

access: 指明Texture的类型。可以是 Stream(视频),也可以是Target一般的类型。


b). 渲染

int SDL_RenderCopy(SDL_Renderer* renderer,

               SDL_Texture*    texture,

               const SDL_Rect* srcrect,

               const SDL_Rect* dstrect)

 


下面说明一下重要参数:

srcrect: 指定 Texture 中要渲染的一部分。如果将 Texture全部输出,可以设置它为 NULL。

dstrect: 指定输出的空间大小。


c). 销毁 SDL_Texture

void SDL_DestroyTexture(SDL_Texture* texture)

 


纹理渲染实战:

复制代码

// SDL.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。

//

#include <iostream>
extern "C" {
#include "SDL.h"
}
int main(int argc, char* argv[])
{
    SDL_Window *window;
    SDL_Renderer *renderer;
    SDL_Texture *texture;
    SDL_Event event;
    SDL_Rect r;
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError());
        return 3;
    }
    window = SDL_CreateWindow("SDL_CreateTexture",
        SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED,
        1024, 768,
        SDL_WINDOW_RESIZABLE);
    r.w = 100;
    r.h = 50;
    renderer = SDL_CreateRenderer(window, -1, 0);
    texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 1024, 768);
    // 跳来跳去的方块
    while (1) {
        SDL_PollEvent(&event);
        if (event.type == SDL_QUIT)
            break;
        r.x = rand() % 500;
        r.y = rand() % 500;
        SDL_SetRenderTarget(renderer, texture);
        SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
        SDL_RenderClear(renderer);
        SDL_RenderDrawRect(renderer, &r);
        SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0x00);
        SDL_RenderFillRect(renderer, &r);
        SDL_SetRenderTarget(renderer, NULL);
        SDL_RenderCopy(renderer, texture, NULL, NULL);
        SDL_RenderPresent(renderer);
    }
    SDL_DestroyRenderer(renderer);
    SDL_Quit();
    return 0;
}


SDL2.0的事件处理机制与案例实战

各位学员:

非常抱歉,这节课录制到最后两分钟的时候,家里的小宝宝突然跑进来,叫了几声。

给您带来了不便,影响了您的体验,敬请谅解。

梅会东

2021.1.6


事件循环

大多数多媒体程序依靠事件系统来处理输入。

SDL为处理输入事件提供了灵活的API。

本质上,SDL将来自设备(如键盘,鼠标或控制器)的输入记录为事件,将它们存储在“事件队列”中。

您可以将此结构视为等待线 - 事件在线的后面排队并从线的前面取出。




在您的程序中,您将始终拥有一个事件(或“游戏”或“主”)循环来处理这些事件并根据输入运行您的程序。每次运行事件循环时,必须从事件队列中拉出每个事件(按顺序)以处理输入。这是通过函数SDL_PollEvent()完成的。此函数从队列中删除第一个事件,将值复制到SDL_Event类型的参数中。如果事件队列为空,则该函数将返回0。


轮询事件后,您可以在逻辑链中使用它来推断输入内容和响应方式。


SDL_Event ev;

bool running = true;


// Main loop

while ( running ) {

// Event loop

while ( SDL_PolLEvent( &ev ) != 0 ) {

// Test members of ev

}


// Wait before next frame

SDL_Delay(100);

}

SDL_Event

SDL_Event包含任何子事件之一。这可以通过使用联合来实现。union描述了结构中的几个互斥数据成员。这意味着子事件类型都存储在同一个内存中,因此SDL_Event可以灵活而不浪费空间。但是,这个系统使语法稍微笨拙 - 访问子事件数据,首先必须访问SDL_Event中的子事件。


例如,访问SDL_KeyboardEvent ..


SDL_Event evt;

SDL_PollEvent( &evt );


if (evt.type == SDL_KEYDOWN) {

switch ( evt.key.sym.sym ) { // Note evt.key accesses the real data, 

// the SDL_KeyboardEvent 

// ...

}

}

退出

当用户希望关闭程序时, 您的事件循环将收到SDL_QUIT类型的事件。这包括按下窗口上的“x”,按ALT + F4,或以其他方式请求程序结束。这不包括结束进程或将CTRL + C发送到控制台 - 这些是不受控制的,立即中止。


因此,当您的程序收到SDL_QUIT事件时,它应该正常关闭(或提示用户提供更多信息)。事件的类型可通过其“类型”成员访问。

SDL_Event ev;
bool running = true;
// Main loop
while ( running ) {
// Event loop
while ( SDL_PolLEvent( &ev ) != 0 ) {
// check event type
switch (ev.type) {
case SDL_QUIT:
// shut down
running = false;
break;
}
}
// Wait before next frame
SDL_Delay(100);
}
#include <stdio.h>
#include <SDL.h>
#undef main
int main(int, char**)///main__event2
{
    SDL_Init(SDL_INIT_EVERYTHING);
    SDL_Window* win = SDL_CreateWindow("yx3sxmeng", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
    SDL_Surface* surface = SDL_LoadBMP("./Hello_world.bmp");
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
    ///1. window-->render(GPU, window),  surface内存信息(bitmap), texture描述信息(render, bitmap|surface);
    ///2. SDL事件循环机制
    bool quit = false;
    SDL_Event ev;
    SDL_Rect rect = { 0,0,800,600 };///目标位置大小(x,y, w,h);
    int sx = 0, sy = 0;
    while (!quit)
    {
        while (SDL_PollEvent(&ev))
        {
            switch (ev.type)
            {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_MOUSEBUTTONDOWN:
                sx = ev.button.x + rect.x;
                sy = ev.button.y + rect.y;
                break;
            case SDL_MOUSEMOTION:
                if (ev.motion.state & SDL_BUTTON_LMASK)
                {
                    rect.x = ev.motion.x - sx;
                    rect.y = ev.motion.y - sy;
                }
                break;
            case SDL_KEYDOWN:
                if (ev.key.keysym.sym == SDLK_LEFT)
                {
                    rect.x -= 10;
                    printf("SDLK_LEFT...");
                }
                else if (ev.key.keysym.sym == SDLK_RIGHT)
                {
                    printf("SDLK_RIGHT...");
                    rect.x += 10;
                }
                else if (ev.key.keysym.sym == SDLK_UP)
                {
                    printf("SDLK_UP...");
                    rect.w += 10;
                    rect.h += 10;
                }
                else if (ev.key.keysym.sym == SDLK_DOWN)
                {
                    printf("SDLK_DOWN...");
                    rect.w -= 10;
                    rect.h -= 10;
                }
                printf("scancode=%d\n", ev.key.keysym.scancode);
                break;
            case SDL_MOUSEWHEEL:
                if (ev.wheel.y > 0)
                {
                    rect.h *= 1.1;
                    rect.w *= 1.1;
                }
                if (ev.wheel.y < 0)
                {
                    rect.w /= 1.1;
                    rect.h /= 1.1;
                }
                break;
            }
        }
        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, NULL, &rect);
        SDL_RenderPresent(renderer);
        SDL_Delay(16);
    }
    //释放资源
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}


好文章,来自【福优学苑@音视频+流媒体】
***【在线视频教程】***