Files
makefile_cpp/SDL/SDL.md
Admin 18c273768f SDL
2023-09-03 15:44:34 +08:00

96 KiB
Raw Permalink Blame History

[TOC]

预备知识

SDL是什么

一个C语言媒体库可用于音视频播放、游戏制作等媒体层。

环境准备

Ubuntu

前置条件

已安装gcc/g++/make如果没安装可用下列命令安装

sudo apt install gcc g++ make

 

再通过以下命令安装SDL开发环境

sudo apt install libsdl2-dev

Makefile

LDLIBS += -lSDL2

main: main.o # 源文件为main.cpp
    $(CXX) $^ -o $@ $(LDFLAGS) $(LDLIBS)

.PHONY: run clean

run: main
    ./$<

clean:
    $(RM) main *.o

Windows

SDL官网地址http://www.libsdl.org/

SDL GitHub地址https://github.com/libsdl-org

MinGW

编译器 MinGW用的w64devkit包gcc/g++/make

                 https://www.mingw-w64.org/

                https://github.com/skeeto/w64devkit/releases

代码编辑器 VSCode

构建方式手动写Makefile编译构建

所以下载的库为这个版本

解压到指定位置之后准备C语言文件main.c测试用先不用理解是啥

#include <stdlib.h>
#include <time.h>
#include <SDL2/SDL.h>

#undef main

#define WIDTH 1000
#define HEIGHT 600



int main()
{
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        SDL_Log("Init Failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Window *win = SDL_CreateWindow(
        "TestWindow", 
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,  
        WIDTH, HEIGHT,
        SDL_WINDOW_SHOWN);

    SDL_Renderer *renderer = SDL_CreateRenderer(win, -1, 0);
    SDL_Rect rect = {100, 100, 100, 100};

    srand(time(0));
    Uint8 r = 255;
    Uint8 g = 255;
    Uint8 b = 255;
    int8_t dx = 8;
    int8_t dy = 10;


    int x = rect.x + rect.w / 2;
    int y = rect.y + rect.h / 2;

    SDL_Event event;
    while (1)
    {
        if (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                break;
            }        

        }

        SDL_RenderClear(renderer);

        rect.x += dx;
        x = rect.x + rect.w / 2;
        if (rect.x + rect.w >= WIDTH || rect.x <= 0)
        {
            dx = -dx;
            rect.y += dy;
            y = rect.y + rect.h / 2;
            if (rect.y + rect.h >= HEIGHT || rect.y <= 0)
            {
                dy = -dy;
            }

            r = rand() % 256;
            g = rand() % 256;
            b = rand() % 256;
        }

        SDL_SetRenderDrawColor(renderer, r, g, b, 255);
        SDL_RenderFillRect(renderer, &rect);

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        for (int i = rect.x; i < rect.x + rect.w; i++)
        {
            for (int j = rect.y; j < rect.y + rect.h; j++)
            {
                if ((i - x) * (i - x) + (j - y) * (j - y) > rect.w * rect.h / 4)
                {
                    SDL_RenderDrawPoint(renderer, i, j);
                }

            }

        }
        SDL_RenderPresent(renderer);

        SDL_Delay(10);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

写如下Makefile

Windows版本Makefile

CXXFLAGS += -ID:/CPP/Libs/SDL2-2.26.5/include #改成你电脑上的include路径
# CXXFLAGS += -fexec-charset=GBK -finput-charset=UTF-8
LDFLAGS +=  -LD:/CPP/Libs/SDL2-2.26.5/lib  # 改成你电脑上的lib路径

CXXFLAGS += -std=c++23 
# 如果不用特别新的C++特性,上面这行不用写
LDLIBS += -lSDL2

LDLIBS += -mwindows # 用于去除运行时命令行窗口


main.exe: main.o
    $(CXX) $^ -o $(basename $@) $(LDFLAGS) $(LDLIBS)

.PHONY: run clean

run: main.exe
    $<

clean:
    $(RM) *.exe *.o

完成之后用下列命令编译运行,正常即可

make run

 

 

 

Visual Studio

如果是用其他IDE则需要自行配置SDL的头文件和库文件引用位置。例如Visual Studio则需要下载SDL2-devel-2.28.2-VC.zip这个版本的库文件,解压到指定位置后再进行配置。

新建一个Visual Studio的项目添加main.c文件内容如下

#include <stdlib.h>
#include <time.h>
#include <SDL.h>

#undef main

#define WIDTH 1000
#define HEIGHT 600

int main()
{
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        SDL_Log("Init Failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Window *win = SDL_CreateWindow(
        "TestWindow", 
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,  
        WIDTH, HEIGHT,
        SDL_WINDOW_SHOWN);

    SDL_Renderer *renderer = SDL_CreateRenderer(win, -1, 0);
    SDL_Rect rect = {100, 100, 100, 100};

    srand(time(0));
    Uint8 r = 255;
    Uint8 g = 255;
    Uint8 b = 255;
    int8_t dx = 8;
    int8_t dy = 10;


    int x = rect.x + rect.w / 2;
    int y = rect.y + rect.h / 2;

    SDL_Event event;
    while (1)
    {
        if (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                break;
            }        

        }

        SDL_RenderClear(renderer);

        rect.x += dx;
        x = rect.x + rect.w / 2;
        if (rect.x + rect.w >= WIDTH || rect.x <= 0)
        {
            dx = -dx;
            rect.y += dy;
            y = rect.y + rect.h / 2;
            if (rect.y + rect.h >= HEIGHT || rect.y <= 0)
            {
                dy = -dy;
            }

            r = rand() % 256;
            g = rand() % 256;
            b = rand() % 256;
        }

        SDL_SetRenderDrawColor(renderer, r, g, b, 255);
        SDL_RenderFillRect(renderer, &rect);

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        for (int i = rect.x; i < rect.x + rect.w; i++)
        {
            for (int j = rect.y; j < rect.y + rect.h; j++)
            {
                if ((i - x) * (i - x) + (j - y) * (j - y) > rect.w * rect.h / 4)
                {
                    SDL_RenderDrawPoint(renderer, i, j);
                }

            }

        }
        SDL_RenderPresent(renderer);

        SDL_Delay(10);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

再配置静态库引用位置头文件引用位置。最后编译运行测试如果正常输出信息则配置正确。其他IDE也是类似配置好库文件、头文件即可。

Visual Studio 2022配置SDL2开发环境

  1. 鼠标右键项目-->属性(Properties)打开属性配置面板)

  2. 路径配置:

    配置(Configuration)选择所有配置(All Configuration),平台(Platform)选择x64(也可以选择Win32需要与后续配置的库对应上)。点击VC++目录(VC++ Directories)分别配置包含目录(Include Directories)和库目录(Library Directories)

    包含目录配置将解压后的SDL中的include目录添加进来点击确定(OK)。

    库目录配置将解压后的SDL中的lib\x64(如果选的平台是Win32则选x86)目录添加进来,点击确定(OK)。

  3. 链接库配置:点击链接器(Linker),编辑添加依赖项(Additional Dependencies)

    输入SDL2.lib点击确定。

Windows上动态链接库找不到的解决办法

  1. 将SDL2.dll文件复制到C:\Windows\System32目录下

  2. 将SDL2.dll所在的目录添加到环境变量

  3. 将SDL2.dll复制到当前项目生成的.exe文件同级目录

SDL基础应用示例

SDL环境初始化与退出

每次使用SDL都需要进行初始化使用结束之后需要退出。用到的函数为

初始化与退出

// 初始化
int SDL_Init(Uint32 flags);
// 退出
void SDL_Quit(void);

初始化成功则返回0失败则返回一个负数。

flags的取值见下表宏定义

例如


使用SDL的C语言文件结构一般如下

#include <SDL2/SDL.h>

#undef main // 加上这句防止一些奇怪的报错


int main()
{
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);

    // 一些操作
    // ....

    SDL_Quit();
    return 0;
}

窗口显示

窗口创建与销毁函数

// 创建函数成功则返回窗口指针失败则返回NULL
SDL_Window * SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags);

// 销毁
void SDL_DestroyWindow(SDL_Window * window);
参数 含义
title 窗口标题UTF-8编码
x 窗口起始位置x坐标 通常为居中SDL_WINDOWPOS_CENTERED或未定义SDL_WINDOWPOS_UNDEFINED
y 窗口起始位置y坐标 通常为居中SDL_WINDOWPOS_CENTERED或未定义SDL_WINDOWPOS_UNDEFINED
w 窗口宽度
h 窗口高度
flags 窗口显示标志可以是0或者是多个其他标志组合
window 由SDL_CreateWindow创建返回的窗口指针

flags常用可取值定义在SDL_video.h中多个值时用|连接


延迟(阻塞)函数

void SDL_Delay(Uint32 ms);

让程序暂停指定的时间,单位为毫秒

完整窗口显示示例

#include <SDL2/SDL.h>

#undef main 

int main()
{
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);

    SDL_Window *win = SDL_CreateWindow(
        "Hello SDL", 
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,  
        600, 400, 
        SDL_WINDOW_SHOWN);

    SDL_Delay(5000);

    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

事件循环

直接用SDL_Delay阻塞程序的话无法响应事件所以需要加入事件循环进行事件监听。通常做法是把事件循环单独写到一个函数当中此时SDL程序结构如下

#include <SDL2/SDL.h>

#define WIDTH 640
#define HEIGHT 480



void event_loop()
{
    SDL_Event event;
    while (1)
    {
        if (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
            case SDL_QUIT:
                return;
            default:
                break;
            }
        }  
    }  
}

#undef main
int main()
{
    if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO) < 0)
    {
        SDL_Log("SDL_Init failed: %s", SDL_GetError());
        return -1;
    }
    SDL_Window *window = SDL_CreateWindow(
            "SDLWindow", 
            SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 
            WIDTH, HEIGHT, 
            SDL_WINDOW_SHOWN);
    if (NULL == window)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }

    event_loop();

    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

结构体SDL_Event

定义于文件 SDL_Events.h中,以下为一些常用成员,如需了解更多请到SDL_Events.h文件中查看

typedef union SDL_Event
{
    Uint32 type;                            // 事件类型,见下
    SDL_CommonEvent common;                 /**< Common event data */
    SDL_DisplayEvent display;               /**< Display event data */
    SDL_WindowEvent window;                 /**< Window event data */
    SDL_KeyboardEvent key;                  /**< Keyboard event data */
    SDL_TextEditingEvent edit;              /**< Text editing event data */
    SDL_TextEditingExtEvent editExt;        /**< Extended text editing event data */
    SDL_TextInputEvent text;                /**< Text input event data */
    SDL_MouseMotionEvent motion;            /**< Mouse motion event data */
    SDL_MouseButtonEvent button;            /**< Mouse button event data */
    SDL_MouseWheelEvent wheel;              /**< Mouse wheel event data */

    SDL_AudioDeviceEvent adevice;           /**< Audio device event data */
    SDL_SensorEvent sensor;                 /**< Sensor event data */
    SDL_QuitEvent quit;                     /**< Quit request event data */
    SDL_UserEvent user;                     /**< Custom event data */
    SDL_SysWMEvent syswm;                   /**< System dependent window event data */
    SDL_TouchFingerEvent tfinger;           /**< Touch finger event data */
    SDL_MultiGestureEvent mgesture;         /**< Gesture event data */
    SDL_DollarGestureEvent dgesture;        /**< Gesture event data */
    SDL_DropEvent drop;                     /**< Drag and drop event data */

    // ....更多内容请查阅SDL_Events.h文件

} SDL_Event;

枚举类型SDL_EventType

事件类型,定义于文件 SDL_Events.h中,以下为一些常用成员,如需了解更多请查阅 SDL_Events.h文件

typedef enum
{
    SDL_QUIT           = 0x100, // 用户请求  退出

    /* 键盘事件 */
    SDL_KEYDOWN        = 0x300, // 键盘按下
    SDL_KEYUP,                  // 键盘弹起
    SDL_TEXTEDITING,            // 
    SDL_TEXTINPUT,              // 文字输入


    /* 鼠标事件 */
    SDL_MOUSEMOTION    = 0x400, // 鼠标运动
    SDL_MOUSEBUTTONDOWN,        // 鼠标键按下
    SDL_MOUSEBUTTONUP,          // 鼠标键松开
    SDL_MOUSEWHEEL,             // 鼠标滚轮滚动

    // .... 更多事件类型请到SDL_Events.h文件查看

} SDL_EventType;

事件检测函数

int SDL_PollEvent(SDL_Event * event)

检测是否有等待处理的事件。如果队列中有事件则把里面的第一个事件从事件队列中移除保存到event这个指针中并返回1。如果已经没有事件则返回0。

event用于接收事件队列中下一事件的指针如果没有等待的事件则用NULL填充。

图形绘制

在屏幕上绘制一个矩形需要用到如下三个函数

// 获取与对应window关联的SDL surface失败则返回NULL
SDL_Surface * SDL_GetWindowSurface(SDL_Window * window);

// 在SDL surface上画一个矩形区域成功返回0 失败返回负数
int SDL_FillRect(SDL_Surface * dst, const SDL_Rect * rect, Uint32 color);

// 将surface的内容更新到屏幕上成功返回0 失败返回负数
int SDL_UpdateWindowSurface(SDL_Window * window)

color为4个字节无符号整数为4通道颜色值ARGB其中第一个字节为Alpha通道即透明度通道R为红色分量通道G为绿色分量通道B为蓝色分量通道。通常用十六进制表示例如0xff00aa8c。用这种方式绘制图形时透明通道是不生效的所以颜色值可以只写三个字节RGB分量例如0xff00bb。

如果不习惯将颜色值写成一整个十六进制数值,可以将各个通道值分开写,则需要用到如下函数进行转换:

// 将RG B三个通道分量转换为一个整数值。三个分量取值范围均为0~255
Uint32 SDL_MapRGB(const SDL_PixelFormat * format, Uint8 r, Uint8 g, Uint8 b);
// 将R, G B, A四个通道分量转为一个整数值。四个分量取值范围均为0~255
Uint32 SDLCALL SDL_MapRGBA(const SDL_PixelFormat * format,
                                           Uint8 r, Uint8 g, Uint8 b,
                                           Uint8 a);

SDL_PixelFormat由以下方式获得与释放

SDL_PixelFormat * format = SDL_AllocFormat(SDL_PIXELFORMAT_BGRA32);

SDL_FreeFormat(format);

SDL_AllocFormat的参数为枚举类型SDL_PixelFormatEnum定义在SDL_pixels.h里,常用的有:

  • 三通道SDL_PIXELFORMAT_RGB24SDL_PIXELFORMAT_BGR24
  • 四通道SDL_PIXELFORMAT_ARGB32SDL_PIXELFORMAT_BGRA32

小端存储的用BGR这个顺序的大端存储的用RGB这个顺序的如果无法判断用哪个可每个顺序试一下看是否能达到想要的效果即可。

完整颜色转换示例:

SDL_PixelFormat * format = SDL_AllocFormat(SDL_PIXELFORMAT_BGR24);

Uint32 color = SDL_MapRGB(format, 255, 0, 111));
SDL_FreeFormat(format);

SDL_Rect

在屏幕上绘制矩形

#include <SDL2/SDL.h>

#define W 1200
#define H 800

#undef main
int main()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Window *win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }

    // 在窗口上绘制一个矩形
    // 1. 获取与窗口关联的Surface
    SDL_Surface *surf = SDL_GetWindowSurface(win);
    if (NULL == surf)
    {
        SDL_Log("SDL_GetWindowSurface failed: %s", SDL_GetError());
        return -1;
    }

    // 2. 定义一个区域
    SDL_Rect rect = {200, 200, 50, 50};

    // 3. 在Surface上进行绘制
    // SDL_FillRect(surf, &rect, SDL_MapRGB(surf->format, 0, 255, 0));
    SDL_FillRect(surf, &rect, 0x00ff00);

    // 4. 将绘制的内容更新到屏幕上
    SDL_UpdateWindowSurface(win);
    

    SDL_Event event;
    while (true)
    {
        if (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                break;
            }
        }

        SDL_FillRect(surf, &rect, 0x000000);
        rect.x += 2;
        SDL_FillRect(surf, &rect, 0x00ff00);
        SDL_UpdateWindowSurface(win);
        SDL_Delay(10);
    }
    
    
    // 4. 释放Surface
    SDL_FreeSurface(surf);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

图片显示

SDL核心库只支持BMP图片的操作。如需操作更多格式的图片需要SDL2_image库添加与使用方式与SDL核心库一样。头文件为#include <SDL2/SDL_image.h>库文件为SDL2_image。这里为了简便不再引入SDL2_image库直接使用BMP图片进行操作。

#include <SDL2/SDL.h>

#define W 1200
#define H 800

#undef main
int main()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Window *win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }

    // 显示BMP图片
    // 1. 获取与窗口关联的Surface
    SDL_Surface *surf = SDL_GetWindowSurface(win);
    if (NULL == surf)
    {
        SDL_Log("SDL_GetWindowSurface failed: %s", SDL_GetError());
        return -1;
    }
    

    // 2. 导入BMP图片
    SDL_Surface *img_surf = SDL_LoadBMP("8.bmp");
    if (NULL == img_surf)
    {
        SDL_Log("SDL_LoadBMP failed: %s", SDL_GetError());
        return -1;
    }

    

    // 3. 将图片Surface复制到窗口Surface上
    SDL_BlitSurface(img_surf, NULL, surf, NULL);
    

    // 4. 将绘制的内容更新到屏幕上
    SDL_UpdateWindowSurface(win);

    SDL_Event event;
    while (true)
    {
        if (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                break;
            }
        }

    }
    
    
    // 4. 释放Surface
    SDL_FreeSurface(img_surf);
    SDL_FreeSurface(surf);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

像素点操作

修改像素点的颜色值

#include <SDL2/SDL.h>

#define W 600
#define H 400

#undef main
int main()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Window *win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }

    // 操作像素点
    // 1. 获取与窗口关联的Surface
    SDL_Surface *surf = SDL_GetWindowSurface(win);
    if (NULL == surf)
    {
        SDL_Log("SDL_GetWindowSurface failed: %s", SDL_GetError());
        return -1;
    }

    
    // 2. 修改像素点颜色值
    Uint32 *px = (Uint32 *)surf->pixels;
    px[20000] = 0xffffff; 

    // 3. 将绘制的内容更新到屏幕上
    SDL_UpdateWindowSurface(win);

    SDL_Event event;
    while (true)
    {
        if (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                break;
            }
        }

    }
    
    
    // 4. 释放Surface
    SDL_FreeSurface(surf);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

随机像素点显示照片

#include <SDL2/SDL.h>
#include <random>


#undef main 

void event_loop()
{
    SDL_Event event;
    while (1)
    {
        if (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
            case SDL_QUIT:
                return;
            default:
                break;
            }
        }

    }

}


typedef struct tagARGB {
    Uint8 b;
    Uint8 g;
    Uint8 r;
    Uint8 a;
} ARGB;
typedef struct tagRGB {
    Uint8 b;
    Uint8 g;
    Uint8 r;
} RGB;

void swap_value(Uint32 *ptr1, Uint32 *ptr2)
{
    Uint32 tmp = *ptr1;
    *ptr1 = *ptr2;
    *ptr2 = tmp;
}

void shuffle_array(Uint32 arr[], Uint32 count)
{
    std::random_device rdv;
    std::default_random_engine rdn(rdv());
    std::uniform_int_distribution<> distrib(0, count);

    Uint32 j = 0;
    for (Uint32 i = 0; i < count; i++)
    {
        j = distrib(rdn);
        if (i != j)
        {
            swap_value(&arr[i], &arr[j]);
        }

    }

}


int main()
{
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);

    SDL_Surface *bmpimg = SDL_LoadBMP("1684826393041.bmp");

    SDL_Window *win = SDL_CreateWindow(
        "像素显示图片", 
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,  
        bmpimg->w, bmpimg->h, 
        SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);

    SDL_Surface *surf = SDL_GetWindowSurface(win);

    ARGB *px = (ARGB *)surf->pixels;

    Uint32 index = 0;
    Uint32 max_num = surf->w * surf->h;
    Uint32 *index_arr = (Uint32 *)malloc(max_num * sizeof(Uint32));
    for (Uint32 i = 0; i < max_num; i++)
    {
        index_arr[i] = i;
    }

    shuffle_array(index_arr, max_num);

    for (int i = max_num - 1; i >= 0; i--)
    {
        px[index_arr[i]].a = 255;
        px[index_arr[i]].r = ((RGB *)bmpimg->pixels)[index_arr[i]].r;
        px[index_arr[i]].g = ((RGB *)bmpimg->pixels)[index_arr[i]].g;
        px[index_arr[i]].b = ((RGB *)bmpimg->pixels)[index_arr[i]].b;

        if (i % 200 == 0)
        {
            SDL_UpdateWindowSurface(win);
            SDL_Delay(1);
        }


    } 




    SDL_UpdateWindowSurface(win);


    event_loop();
    free(index_arr);
    SDL_FreeSurface(surf);
    SDL_DestroyWindow(win);
    SDL_FreeSurface(bmpimg);
    SDL_Quit();
    return 0;
}

绘制玫瑰

#include <math.h>
#include <SDL2/SDL.h>

#undef main

// 定义全局变量
int    rosesize = 500;
int    h = -250;

// 定义结构体
struct DOT
{
    double x;
    double y;
    double z;
    double r;    // 红色
    double g;    // 绿色
    // b(蓝色) 通过 r 计算
};



// 计算点
bool calc(double a, double b, double c, DOT &d)
{
    double j, n, o, w, z;

    if(c > 60)                // 花柄
    {
        d.x = sin(a * 7) * (13 + 5 / (0.2 + pow(b * 4, 4))) - sin(b) * 50;
        d.y = b * rosesize + 50;
        d.z = 625 + cos(a * 7) * (13 + 5 / (0.2 + pow(b * 4, 4))) + b * 400;
        d.r = a * 1 - b / 2;
        d.g = a;
        return true;
    }

    double A = a * 2 - 1;
    double B = b * 2 - 1;
    if(A * A + B * B < 1)
    {
        if(c > 37)            // 叶
        {
            j = (int(c) & 1);
            n = j ? 6 : 4;
            o = 0.5 / (a + 0.01) + cos(b * 125) * 3 - a * 300;
            w = b * h;

            d.x = o * cos(n) + w * sin(n) + j * 610 - 390;
            d.y = o * sin(n) - w * cos(n) + 550 - j * 350;
            d.z = 1180 + cos(B + A) * 99 - j * 300;
            d.r = 0.4 - a * 0.1 + pow(1 - B * B, -h * 6) * 0.15 - a * b * 0.4 + cos(a + b) / 5 + pow(cos((o * (a + 1) + (B > 0 ? w : -w)) / 25), 30) * 0.1 * (1 - B * B);
            d.g = o / 1000 + 0.7 - o * w * 0.000003;
            return true;
        }
        if(c > 32)            // 花萼
        {
            c = c * 1.16 - 0.15;
            o = a * 45 - 20;
            w = b * b * h;
            z = o * sin(c) + w * cos(c) + 620;

            d.x = o * cos(c) - w * sin(c);
            d.y = 28 + cos(B * 0.5) * 99 - b * b * b * 60 - z / 2 - h;
            d.z = z;
            d.r = (b * b * 0.3 + pow((1 - (A * A)), 7) * 0.15 + 0.3) * b;
            d.g = b * 0.7;
            return true;
        }

        // 花
        o = A * (2 - b) * (80 - c * 2);
        w = 99 - cos(A) * 120 - cos(b) * (-h - c * 4.9) + cos(pow(1 - b, 7)) * 50 + c * 2;
        z = o * sin(c) + w * cos(c) + 700;

        d.x = o * cos(c) - w * sin(c);
        d.y = B * 99 - cos(pow(b, 7)) * 50 - c / 3 - z / 1.35 + 450;
        d.z = z;
        d.r = (1 - b / 1.2) * 0.9 + a * 0.1;
        d.g = pow((1 - b), 20) / 4 + 0.05;
        return true;
    }

    return false;
}




int main()
{
    short    *zBuffer;
    int        x, y, z, zBufferIndex;
    DOT        dot;

    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        SDL_Log("Init Failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Window *win = SDL_CreateWindow(
        "花花", 
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,  
        640, 480, 
        SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("Window create failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Surface *win_surface = SDL_GetWindowSurface(win);

    SDL_Rect rect = {0, 0, win_surface->w, win_surface->h};
    SDL_FillRect(win_surface, &rect, 0xffffffff);
    SDL_UpdateWindowSurface(win);


    // 初始化 z-buffer
    zBuffer = new short[rosesize * rosesize];
    memset(zBuffer, 0, sizeof(short) * rosesize * rosesize);


     SDL_Event event;
    while (1)
    {
        if (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                break;
            }
        }

        for(int i = 0; i < 1000; i++)
        {
            if(calc(double(rand()) / RAND_MAX, double(rand()) / RAND_MAX, rand() % 46 / 0.74, dot))
            {
                z = int(dot.z + 0.5);
                x = int(dot.x * rosesize / z - h + 0.5);
                y = int(dot.y * rosesize / z - h + 0.5);
                if (y >= rosesize) continue;

                zBufferIndex = y * rosesize + x;

                if(!zBuffer[zBufferIndex] || zBuffer[zBufferIndex] > z)
                {
                    zBuffer[zBufferIndex] = z;

                    // 画点
                    int r = ~int((dot.r * h));                if (r < 0) r = 0;    if (r > 255) r = 255;
                    int g = ~int((dot.g * h));                if (g < 0) g = 0;    if (g > 255) g = 255;
                    int b = ~int((dot.r * dot.r * -80));    if (b < 0) b = 0;    if (b > 255) b = 255;
                    int index = (x + 50) + (y - 20) * win_surface->w;
                    ((Uint32 *)win_surface->pixels)[index] = SDL_MapRGB(win_surface->format, r, g, b);

                }
            }
        }
        SDL_UpdateWindowSurface(win);
        SDL_Delay(1);
    }

    delete []zBuffer;
    SDL_FreeSurface(win_surface);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

 

 

渲染

渲染器创建与清屏

#include <SDL2/SDL.h>

#define W 800
#define H 600

#undef main
int main()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Window *win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器与清屏
    // 1. 创建渲染器
    SDL_Renderer *rdr = SDL_CreateRenderer(win, -1, 0);
    // 2. 设置渲染颜色
    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);

    // 3. 清除屏幕
    SDL_RenderClear(rdr);

    // 4. 渲染呈现 
    SDL_RenderPresent(rdr);   
    


    SDL_Event event;
    while (true)
    {
        if (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                break;
            }
        }

    }
    
    
    // 5. 销毁渲染器
    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

绘制点、线

#include <deque>
#include <SDL2/SDL.h>

#define W 800
#define H 600

#undef main
int main()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Window *win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器与清屏
    SDL_Renderer *rdr  = SDL_CreateRenderer(win, -1, 0);
    if (NULL == rdr)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        return -1;
    }
    // 设置渲染颜色
    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
    // 清除屏幕
    SDL_RenderClear(rdr);

    // 渲染点、线
    SDL_SetRenderDrawColor(rdr, 0, 0, 0, 255);
    SDL_RenderDrawPoint(rdr, 200, 200);
    for (int i = 200; i < 300; i+=5)
    {
        SDL_RenderDrawPoint(rdr, i, 200);
    }

    SDL_RenderDrawLine(rdr, 0, 0, 200, 250);

    SDL_Point pt[5] = {{0, 0}, {100, 100}, {100, 300}, {200, 100}, {250, 190}};
    SDL_RenderDrawLines(rdr, pt, 5);
    

    // 渲染呈现
    SDL_RenderPresent(rdr);
    // 练习绘制一个19*19的围棋棋盘


    SDL_Event event;
    while (true)
    {
        if (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                break;
            }
        }

    }
    
    
    // 销毁渲染器
    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

绘制、填充矩形区域

#include <SDL2/SDL.h>

#define W 800
#define H 600

#undef main
int main()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Window *win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器与清屏
    SDL_Renderer *rdr  = SDL_CreateRenderer(win, -1, 0);
    if (NULL == rdr)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        return -1;
    }
    // 设置渲染颜色
    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
    // 清除屏幕
    SDL_RenderClear(rdr);
    SDL_SetRenderDrawBlendMode(rdr, SDL_BLENDMODE_BLEND);

    // 渲染矩形
    SDL_SetRenderDrawColor(rdr, 0, 0, 0, 255);
    SDL_Rect rect = {200, 200, 100, 100};
    // SDL_RenderDrawRect(rdr, &rect);
    SDL_RenderFillRect(rdr, &rect);
    

    // 渲染呈现
    SDL_RenderPresent(rdr);
    // 练习:思考一下如何实现贪吃蛇一条蛇移动的效果
    //       可以借助一下vector/deque等容器


    SDL_Event event;
    while (true)
    {
        if (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                break;
            }
        }

        // 设置渲染颜色
        SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
        // 清除屏幕
        // SDL_RenderClear(rdr);
        SDL_RenderFillRect(rdr, &rect);
        SDL_SetRenderDrawColor(rdr, 0, 0, 0, 255);
        rect.x ++;
        // SDL_RenderDrawRect(rdr, &rect);
        SDL_RenderFillRect(rdr, &rect);
        

        // 渲染呈现
        SDL_RenderPresent(rdr);
        SDL_Delay(10);
    }
    
    
    // 销毁渲染器
    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

图片渲染

#include <SDL2/SDL.h>

#define W 1369
#define H 768

#undef main
int main()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Window *win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器与清屏
    SDL_Renderer *rdr  = SDL_CreateRenderer(win, -1, 0);
    if (NULL == rdr)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        return -1;
    }
    // 设置渲染颜色
    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
    // 清除屏幕
    SDL_RenderClear(rdr);
    
    // 渲染图片
    // 1. 导入图片
    SDL_Surface *img_surf = SDL_LoadBMP("../SDL/img/2.bmp"); // 1296*864
    
    if (NULL == img_surf)
    {
        SDL_Log("SDL_LoadBMP failed: %s", SDL_GetError());
        return -1;
    }
    SDL_SetWindowSize(win, img_surf->w, img_surf->h);
    

    // 2. 创建Texture
    SDL_Texture *texture = SDL_CreateTextureFromSurface(rdr, img_surf);
    if (NULL == texture)
    {
        SDL_Log("SDL_CreateTextureFromSurface failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Rect rect = {200, 200, 648, 432};
    // 3. 复制Texture
    SDL_RenderCopy(rdr, texture, NULL, &rect);
    

    // 渲染呈现
    SDL_RenderPresent(rdr);
    


    SDL_Event event;
    while (true)
    {
        if (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                break;
            }
        }
    }
    
    
    // 5. 销毁Texture
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(img_surf);
    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

图片缩放、旋转

#include <SDL2/SDL.h>

#define W 1369
#define H 768

#undef main
int main()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Window *win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器与清屏
    SDL_Renderer *rdr  = SDL_CreateRenderer(win, -1, 0);
    if (NULL == rdr)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        return -1;
    }
    // 设置渲染颜色
    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
    // 清除屏幕
    SDL_RenderClear(rdr);
    
    // 渲染图片
    // 1. 导入图片
    SDL_Surface *img_surf = SDL_LoadBMP("../SDL/img/2.bmp"); // 1296*864
    
    if (NULL == img_surf)
    {
        SDL_Log("SDL_LoadBMP failed: %s", SDL_GetError());
        return -1;
    }
    SDL_SetWindowSize(win, img_surf->w, img_surf->h);
    

    // 2. 创建Texture
    SDL_Texture *texture = SDL_CreateTextureFromSurface(rdr, img_surf);
    if (NULL == texture)
    {
        SDL_Log("SDL_CreateTextureFromSurface failed: %s", SDL_GetError());
        return -1;
    }

    SDL_Rect rect = {200, 200, 648, 432};
    SDL_Point pt = {0, 0};
    // 3. 复制Texture
    SDL_RenderCopyEx(rdr, texture, NULL, &rect, 90, NULL, SDL_FLIP_NONE);
    

    // 渲染呈现
    SDL_RenderPresent(rdr);
    


    SDL_Event event;
    while (true)
    {
        if (SDL_PollEvent(&event))
        {
            if (event.type == SDL_QUIT)
            {
                break;
            }
        }
    }
    
    
    // 5. 销毁Texture
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(img_surf);
    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

代码结构整理

#include <SDL2/SDL.h>

#define W 720
#define H 640

SDL_Window *win = NULL;
SDL_Renderer *rdr  = NULL;
SDL_Rect rect = {100, 100, 200, 100};


int init()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器
    rdr  = SDL_CreateRenderer(win, -1, 0);
    if (NULL == rdr)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        return -1;
    }

    return 0;
}


void deinit()
{
    // 销毁

    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
}

int draw()
{
    // 设置渲染颜色
    SDL_SetRenderDrawColor(rdr, 100, 100, 100, 100);
    // 清除屏幕
    SDL_RenderClear(rdr);
    
    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
    SDL_RenderFillRect(rdr, &rect);

    // 渲染呈现
    SDL_RenderPresent(rdr);
    return 0;
}


void event_loop()
{
    SDL_Event event;
    while (true)
    {
        while (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
            case SDL_QUIT:
                return;
            
            default:
                break;
            }
        }
    }
}


#undef main
int main()
{
    if (init() < 0)
    {
        return -1;
    }
    
    draw();

    event_loop();
    
    deinit();
    return 0;
}

窗口事件

#include <SDL2/SDL.h>

#define W 720
#define H 640

SDL_Window *win = NULL;
SDL_Renderer *rdr  = NULL;
SDL_Rect rect = {100, 100, 200, 100};


int init()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器
    rdr  = SDL_CreateRenderer(win, -1, 0);
    if (NULL == rdr)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        return -1;
    }

    return 0;
}


void deinit()
{
    // 销毁

    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
}

int draw()
{
    // 设置渲染颜色
    SDL_SetRenderDrawColor(rdr, 100, 100, 100, 100);
    // 清除屏幕
    SDL_RenderClear(rdr);
    
    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
    SDL_RenderFillRect(rdr, &rect);

    // 渲染呈现
    SDL_RenderPresent(rdr);
    return 0;
}


void event_loop()
{
    SDL_Event event;
    while (true)
    {
        while (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
            case SDL_QUIT:
                return;
            case SDL_WINDOWEVENT:
                if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
                {
                    draw();
                }
                break;
            
            default:
                break;
            }
        }
    }
}


#undef main
int main()
{
    if (init() < 0)
    {
        return -1;
    }
    
    draw();

    event_loop();
    
    deinit();
    return 0;
}

鼠标事件

移动

#include <SDL2/SDL.h>

#define W 720
#define H 640

SDL_Window *win = NULL;
SDL_Renderer *rdr  = NULL;
SDL_Rect rect = {100, 100, 200, 100};


int init()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器
    rdr  = SDL_CreateRenderer(win, -1, 0);
    if (NULL == rdr)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        return -1;
    }

    return 0;
}


void deinit()
{
    // 销毁

    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
}

int draw()
{
    // 设置渲染颜色
    SDL_SetRenderDrawColor(rdr, 100, 100, 100, 100);
    // 清除屏幕
    SDL_RenderClear(rdr);
    
    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
    SDL_RenderFillRect(rdr, &rect);

    // 渲染呈现
    SDL_RenderPresent(rdr);
    return 0;
}


void event_loop()
{
    SDL_Event event;
    while (true)
    {
        while (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
            case SDL_QUIT:
                return;
            case SDL_WINDOWEVENT:
                if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
                {
                    draw();
                }
                break;
            case SDL_MOUSEMOTION:
                SDL_Log("x = %d, y = %d", event.motion.x, event.motion.y);
                rect.x = event.motion.x - 100;
                rect.y = event.motion.y - 50;
                draw();
                break;
            
            default:
                break;
            }
        }
    }
}


#undef main
int main()
{
    if (init() < 0)
    {
        return -1;
    }
    
    draw();

    event_loop();
    
    deinit();
    return 0;
}

按键

#include <SDL2/SDL.h>

#define W 720
#define H 640

SDL_Window *win = NULL;
SDL_Renderer *rdr  = NULL;
SDL_Rect rect = {100, 100, 200, 100};


int init()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器
    rdr  = SDL_CreateRenderer(win, -1, 0);
    if (NULL == rdr)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        return -1;
    }

    return 0;
}


void deinit()
{
    // 销毁

    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
}

int draw()
{
    // 设置渲染颜色
    SDL_SetRenderDrawColor(rdr, 100, 100, 100, 100);
    // 清除屏幕
    SDL_RenderClear(rdr);
    
    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
    SDL_RenderFillRect(rdr, &rect);

    // 渲染呈现
    SDL_RenderPresent(rdr);
    return 0;
}


void event_loop()
{
    SDL_Event event;
    while (true)
    {
        while (SDL_PollEvent(&event))
        {
            SDL_Point pt;
            switch (event.type)
            {
            case SDL_QUIT:
                return;
            case SDL_WINDOWEVENT:
                if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
                {
                    draw();
                }
                break;
            case SDL_MOUSEMOTION:
                // SDL_Log("x = %d, y = %d", event.motion.x, event.motion.y);
                // rect.x = event.motion.x - 100;
                // rect.y = event.motion.y - 50;
                // draw();
                break;
            case SDL_MOUSEBUTTONDOWN:
                SDL_Log("SDL_MOUSEBUTTONDOWN x = %d, y = %d, button = %d, clicks = %d", 
                    event.button.x, event.button.y, event.button.button, event.button.clicks);
                pt = {event.button.x, event.button.y};
                if (SDL_PointInRect(&pt, &rect))
                {
                    SDL_SetRenderDrawColor(rdr, 0, 200, 0, 255);
                    SDL_RenderFillRect(rdr, &rect);

                    // 渲染呈现
                    SDL_RenderPresent(rdr);
                }
                
                
                break;
            case SDL_MOUSEBUTTONUP:
                SDL_Log("SDL_MOUSEBUTTONUP x = %d, y = %d, button = %d, clicks = %d", 
                    event.button.x, event.button.y, event.button.button, event.button.clicks);
                pt = {event.button.x, event.button.y};
                if (SDL_PointInRect(&pt, &rect))
                {
                    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
                    SDL_RenderFillRect(rdr, &rect);

                    // 渲染呈现
                    SDL_RenderPresent(rdr);
                }
                break;
            
            default:
                break;
            }
        }
    }
}


#undef main
int main()
{
    if (init() < 0)
    {
        return -1;
    }
    
    draw();

    event_loop();
    
    deinit();
    return 0;
}

键盘事件

#include <SDL2/SDL.h>

#define W 720
#define H 640

SDL_Window *win = NULL;
SDL_Renderer *rdr  = NULL;
SDL_Rect rect = {100, 100, 50, 50};
int dx = 1;
int dy = 0;


int init()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器
    rdr  = SDL_CreateRenderer(win, -1, 0);
    if (NULL == rdr)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        return -1;
    }

    return 0;
}


void deinit()
{
    // 销毁

    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
}

int draw()
{
    // 设置渲染颜色
    SDL_SetRenderDrawColor(rdr, 100, 100, 100, 100);
    // 清除屏幕
    SDL_RenderClear(rdr);
    
    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
    SDL_RenderFillRect(rdr, &rect);

    // 渲染呈现
    SDL_RenderPresent(rdr);
    return 0;
}


void event_loop()
{
    SDL_Event event;
    while (true)
    {
        while (SDL_PollEvent(&event))
        {
            SDL_Point pt;
            switch (event.type)
            {
            case SDL_QUIT:
                return;
            case SDL_WINDOWEVENT:
                if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
                {
                    draw();
                }
                break;
            case SDL_MOUSEMOTION:
                // SDL_Log("x = %d, y = %d", event.motion.x, event.motion.y);
                // rect.x = event.motion.x - 100;
                // rect.y = event.motion.y - 50;
                // draw();
                break;
            case SDL_MOUSEBUTTONDOWN:
                SDL_Log("SDL_MOUSEBUTTONDOWN x = %d, y = %d, button = %d, clicks = %d", 
                    event.button.x, event.button.y, event.button.button, event.button.clicks);
                pt = {event.button.x, event.button.y};
                if (SDL_PointInRect(&pt, &rect))
                {
                    SDL_SetRenderDrawColor(rdr, 0, 200, 0, 255);
                    SDL_RenderFillRect(rdr, &rect);

                    // 渲染呈现
                    SDL_RenderPresent(rdr);
                }
                
                
                break;
            case SDL_MOUSEBUTTONUP:
                SDL_Log("SDL_MOUSEBUTTONUP x = %d, y = %d, button = %d, clicks = %d", 
                    event.button.x, event.button.y, event.button.button, event.button.clicks);
                pt = {event.button.x, event.button.y};
                if (SDL_PointInRect(&pt, &rect))
                {
                    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
                    SDL_RenderFillRect(rdr, &rect);

                    // 渲染呈现
                    SDL_RenderPresent(rdr);
                }
                break;
            case SDL_KEYDOWN:
                if (event.key.keysym.sym == SDLK_UP)
                {
                    SDL_Log("UP");
                    dy = -1;
                    dx = 0;
                }
                if (event.key.keysym.sym == SDLK_DOWN)
                {
                    SDL_Log("DOWN");
                    dy = 1;
                    dx = 0;
                }
                if (event.key.keysym.sym == SDLK_LEFT)
                {
                    SDL_Log("LEFT");
                    dx = -1;
                    dy = 0;
                }
                if (event.key.keysym.sym == SDLK_RIGHT)
                {
                    SDL_Log("RIGHT");
                    dx = 1;
                    dy = 0;
                }
                
                break;
            case SDL_KEYUP:
                break;
            case SDL_TEXTEDITING:
                SDL_Log("Edit %s", event.edit.text);
                break;
            
            case SDL_TEXTINPUT:
                SDL_Log("Input %s", event.text.text); 
                break;
            
            default:
                break;
            }
        }

        rect.x += dx;
        rect.y += dy;
        draw();
        SDL_Delay(10);
    }
}


#undef main
int main()
{
    if (init() < 0)
    {
        return -1;
    }
    
    draw();

    event_loop();
    
    deinit();
    return 0;
}

碰撞以及范围检测

播放声音

#include <SDL2/SDL.h>

#define W 1027
#define H 761

SDL_Window *win = NULL;
SDL_Renderer *rdr  = NULL;
SDL_Rect rect = {100, 100, 50, 50};
int dx = 1;
int dy = 0;

Uint8 *audio_buf;
Uint32 audio_len;
Uint32 audio_pos = 0;
SDL_AudioDeviceID device_id;


int init()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器
    rdr  = SDL_CreateRenderer(win, -1, 0);
    if (NULL == rdr)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        return -1;
    }

    return 0;
}

void deinit()
{
    // 销毁
    SDL_CloseAudioDevice(device_id);
    SDL_FreeWAV(audio_buf);
    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
}

int draw()
{
    // 设置渲染颜色
    SDL_SetRenderDrawColor(rdr, 100, 100, 100, 100);
    // 清除屏幕
    SDL_RenderClear(rdr);
    
    SDL_Surface *surf = SDL_LoadBMP("10.bmp");
    SDL_Texture *texture = SDL_CreateTextureFromSurface(rdr, surf);
    SDL_RenderCopy(rdr, texture, NULL, NULL);

    // 渲染呈现
    SDL_RenderPresent(rdr);
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(surf);
    return 0;
}

void event_loop()
{
    SDL_Event event;
    while (true)
    {
        while (SDL_PollEvent(&event))
        {
            SDL_Point pt;
            switch (event.type)
            {
            case SDL_QUIT:
                return;
            case SDL_WINDOWEVENT:
                if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
                {
                    draw();
                }
                break;
            case SDL_MOUSEMOTION:
                // SDL_Log("x = %d, y = %d", event.motion.x, event.motion.y);
                // rect.x = event.motion.x - 100;
                // rect.y = event.motion.y - 50;
                // draw();
                break;
            case SDL_MOUSEBUTTONDOWN:
                pt = {event.button.x, event.button.y};
                if (SDL_PointInRect(&pt, &rect))
                {
                    SDL_SetRenderDrawColor(rdr, 0, 200, 0, 255);
                    SDL_RenderFillRect(rdr, &rect);

                    // 渲染呈现
                    SDL_RenderPresent(rdr);
                }
                
                
                break;
            case SDL_MOUSEBUTTONUP:
                pt = {event.button.x, event.button.y};
                if (SDL_PointInRect(&pt, &rect))
                {
                    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
                    SDL_RenderFillRect(rdr, &rect);

                    // 渲染呈现
                    SDL_RenderPresent(rdr);
                }
                break;
            case SDL_KEYDOWN:
                if (event.key.keysym.sym == SDLK_UP)
                {
                    dy = -1;
                    dx = 0;
                }
                if (event.key.keysym.sym == SDLK_DOWN)
                {
                    dy = 1;
                    dx = 0;
                }
                if (event.key.keysym.sym == SDLK_LEFT)
                {
                    dx = -1;
                    dy = 0;
                }
                if (event.key.keysym.sym == SDLK_RIGHT)
                {
                    dx = 1;
                    dy = 0;
                }
                
                break;
            case SDL_KEYUP:
                break;
            case SDL_TEXTEDITING:
                break;
            
            case SDL_TEXTINPUT:
                break;
            
            default:
                break;
            }
        }

        // draw();
        SDL_Delay(10);
    }
}



void callback(void *userdata, Uint8 * stream, int len)
{
    int remain = audio_len - audio_pos;
    if (remain > len)
    {
        SDL_memcpy(stream, audio_buf + audio_pos, len);
        audio_pos += len;
    }
    else
    {
        SDL_memcpy(stream, audio_buf + audio_pos, remain);
        audio_pos = 0;
    }
    
}


void play_wav()
{
    SDL_AudioSpec audio_spec;

    
    // 1. 导入WAV文件
    SDL_LoadWAV("1.wav", &audio_spec, &audio_buf, &audio_len);

    // 2. 定义播放回调函数
    audio_spec.userdata = (void *)"这是外部传进来的数据";
    audio_spec.callback = callback;
    
    // 3. 打开音频设备
    device_id = SDL_OpenAudioDevice(NULL, 0, &audio_spec, NULL, 0);

    // 4. 开始播放
    SDL_PauseAudioDevice(device_id, 0);

    // 5. 释放资源 关闭设备
}


#undef main
int main()
{
    if (init() < 0)
    {
        return -1;
    }
    
    draw();
    play_wav();

    event_loop();
    
    deinit();
    return 0;
}

动画帧率

#include <SDL2/SDL.h>

#define W 1280
#define H 720
#define FR 25
#define FT 1000 / FR

SDL_Window *win = NULL;
SDL_Renderer *rdr  = NULL;
SDL_Rect rect = {100, 100, 50, 50};
int dx = 1;
int dy = 0;

int index = 0;

Uint8 *audio_buf;
Uint32 audio_len;
Uint32 audio_pos = 0;
SDL_AudioDeviceID device_id;



int init()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器
    rdr  = SDL_CreateRenderer(win, -1, 0);
    if (NULL == rdr)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        return -1;
    }

    return 0;
}

void deinit()
{
    // 销毁
    SDL_FreeWAV(audio_buf);
    SDL_CloseAudioDevice(device_id);
    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
}

int draw()
{
    // 设置渲染颜色
    SDL_SetRenderDrawColor(rdr, 100, 100, 100, 100);
    // 清除屏幕
    SDL_RenderClear(rdr);

    char file[20] = {0};
    if (index > 1736)
    {
        return -1;
    }
    
    SDL_snprintf(file, 20, "img/%d.bmp", index++);
    
    SDL_Surface *surf = SDL_LoadBMP(file);
    SDL_Texture *texture = SDL_CreateTextureFromSurface(rdr, surf);
    SDL_RenderCopy(rdr, texture, NULL, NULL);

    // 渲染呈现
    SDL_RenderPresent(rdr);
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(surf);
    return 0;
}

void event_loop()
{
    SDL_Event event;
    Uint64 start, end;
    int delay;
    while (true)
    {
        start = SDL_GetTicks64();
        while (SDL_PollEvent(&event))
        {
            SDL_Point pt;
            switch (event.type)
            {
            case SDL_QUIT:
                return;
            case SDL_WINDOWEVENT:
                if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
                {
                    draw();
                }
                break;
            case SDL_MOUSEMOTION:
                // SDL_Log("x = %d, y = %d", event.motion.x, event.motion.y);
                // rect.x = event.motion.x - 100;
                // rect.y = event.motion.y - 50;
                // draw();
                break;
            case SDL_MOUSEBUTTONDOWN:
                pt = {event.button.x, event.button.y};
                if (SDL_PointInRect(&pt, &rect))
                {
                    SDL_SetRenderDrawColor(rdr, 0, 200, 0, 255);
                    SDL_RenderFillRect(rdr, &rect);

                    // 渲染呈现
                    SDL_RenderPresent(rdr);
                }
                
                
                break;
            case SDL_MOUSEBUTTONUP:
                pt = {event.button.x, event.button.y};
                if (SDL_PointInRect(&pt, &rect))
                {
                    SDL_SetRenderDrawColor(rdr, 0, 255, 0, 255);
                    SDL_RenderFillRect(rdr, &rect);

                    // 渲染呈现
                    SDL_RenderPresent(rdr);
                }
                break;
            case SDL_KEYDOWN:
                if (event.key.keysym.sym == SDLK_UP)
                {
                    dy = -1;
                    dx = 0;
                }
                if (event.key.keysym.sym == SDLK_DOWN)
                {
                    dy = 1;
                    dx = 0;
                }
                if (event.key.keysym.sym == SDLK_LEFT)
                {
                    dx = -1;
                    dy = 0;
                }
                if (event.key.keysym.sym == SDLK_RIGHT)
                {
                    dx = 1;
                    dy = 0;
                }
                
                break;
            case SDL_KEYUP:
                break;
            case SDL_TEXTEDITING:
                break;
            
            case SDL_TEXTINPUT:
                break;
            
            default:
                break;
            }
        }

        draw();
        end = SDL_GetTicks64();

        delay = FT - (end -start);
        if (delay > 0)
        {
            SDL_Delay(delay);
        }
        
        
    }
}


void callback(void *userdata, Uint8 * stream, int len)
{
    int remain = audio_len - audio_pos;
    if (remain > len)
    {
        SDL_memcpy(stream, audio_buf + audio_pos, len);
        audio_pos += len;
    }
    else
    {
        SDL_memcpy(stream, audio_buf + audio_pos, remain);
        audio_pos = 0;
    }
    
}

void play_wav()
{
    SDL_AudioSpec audio_spec;
    // 1. 导入WAV文件
    if (SDL_LoadWAV("2.wav", &audio_spec, &audio_buf, &audio_len) == NULL)
    {
        SDL_Log("SDL_LoadWAV failed: %s", SDL_GetError());
        return;
    }

    // 2. 定义播放回调函数
    audio_spec.callback = callback;

    audio_spec.userdata = (void *)"这是外部传进来的数据";
    
    // 3. 打开音频设备
    device_id = SDL_OpenAudioDevice(NULL, 0, &audio_spec, NULL, 0);

    // 4. 开始播放
    SDL_PauseAudioDevice(device_id, 0);

    // 5. 释放资源 关闭设备
}


#undef main
int main()
{
    if (init() < 0)
    {
        return -1;
    }
    
    draw();
    play_wav();

    event_loop();
    
    deinit();
    return 0;
}

其他模块使用

其他模块的使用方式同核心模块一样,下载对应的库文件,将头文件、库文件合并到核心库的安装目录,在配置中添加对应的库即可。

CXXFLAGS += -IC:/Users/YAN/Desktop/SDL/lib/SDL2-devel-2.28.2-mingw/SDL2-2.28.2/x86_64-w64-mingw32/include #改成你电脑上的include路径
# CXXFLAGS += -fexec-charset=GBK -finput-charset=UTF-8
LDFLAGS +=  -LC:/Users/YAN/Desktop/SDL/lib/SDL2-devel-2.28.2-mingw/SDL2-2.28.2/x86_64-w64-mingw32/lib 

CXXFLAGS += -std=c++23 

LDLIBS += -lSDL2 -lSDL2_ttf # 链接库时加上对应的模块

LDLIBS += -mwindows 


main.exe: main.o
	$(CXX) $^ -o $(basename $@) $(LDFLAGS) $(LDLIBS)

.PHONY: run clean

run: main.exe
	$<

clean:
	$(RM) *.exe *.o

文字模块示例

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

#define W 1280
#define H 720

SDL_Window *win = NULL;
SDL_Renderer *rdr  = NULL;


int init()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("Init failed: %s", SDL_GetError());
        return -1;
    }

    win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        W, H, SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    if (NULL == win)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }


    // 创建渲染器
    rdr  = SDL_CreateRenderer(win, -1, 0);
    if (NULL == rdr)
    {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        return -1;
    }

    return 0;
}

void deinit()
{
    // 销毁
    SDL_FreeWAV(audio_buf);
    SDL_CloseAudioDevice(device_id);
    SDL_DestroyRenderer(rdr);
    SDL_DestroyWindow(win);
    SDL_Quit();
}



void event_loop()
{
    SDL_Event event;
    while (true)
    {
        while (SDL_PollEvent(&event))
        {
            SDL_Point pt;
            switch (event.type)
            {
            case SDL_QUIT:
                return;
            
            default:
                break;
            }
        }

    }
}

void draw_text()
{
    // 1. 初始化TTF
    if (TTF_Init() < 0)
    {
        SDL_Log("TTF_Init failed: %s", TTF_GetError());
        return ;
    }

    // 2. 打开字体
    TTF_Font *font = TTF_OpenFont("C:\\Windows\\Fonts\\simfang.ttf", 48);
    if (!font)
    {
        SDL_Log("TTF_OpenFont failed: %s", TTF_GetError());
        return ;
    }

    // 3. 渲染字体
    SDL_Surface *txt_surf = TTF_RenderUTF8_LCD(font, "你好Hello", {255, 255, 255, 50}, {0, 0, 255, 0});
    if (!txt_surf)
    {
        SDL_Log("TTF_RenderText_Solid failed: %s", TTF_GetError());
        return ;
    }

    // 4. 获取与窗口关联的Surface
    SDL_Surface *surf = SDL_GetWindowSurface(win);
    if (!surf)
    {
        SDL_Log("SDL_GetWindowSurface failed: %s", SDL_GetError());
        return ;
    }

    // 5. 将字体Surface复制到窗口Surface上
    SDL_Rect rect = {500, 300, txt_surf->w, txt_surf->h};
    SDL_BlitSurface(txt_surf, NULL, surf, &rect);

    // 6. 更新窗口
    SDL_UpdateWindowSurface(win);

    // 7. 释放与销毁资源
    SDL_FreeSurface(surf);
    SDL_FreeSurface(txt_surf);
    TTF_CloseFont(font);
    TTF_Quit();
}

#undef main
int main()
{
    if (init() < 0)
    {
        return -1;
    }
    
    draw_text();

    event_loop();
    
    deinit();
    return 0;
}

关于文字渲染的几种类型: *_Solid: 使用纯色填充,速度快,文字渲染质量不佳 *_Shaded: 使用纯色填充,添加一定阴影效果 *_Blended: 使用抗锯齿算法高质量渲染 *_LCD: 针对LCD显示器优化渲染

多线程操作

SDL常用函数、结构体、枚举、宏定义

函数

SDL_Init

声明

// SDL.h
int SDL_Init(Uint32 flags);

功能

初始化SDL库子系统

参数

接收参数为宏定义,见下表

flags取值 初始化系统
SDL_INIT_VIDEO 视频子系统,会自动初始化事件子系统
SDL_INIT_AUDIO 音频子系统
SDL_INIT_TIMER 定时器子系统
SDL_INIT_EVENTS 事件子系统
SDL_INIT_JOYSTICK 操纵杆子系统,自动初始化事件子系统
SDL_INIT_HAPTIC 触觉子系统
SDL_INIT_GAMECONTROLLER 控制子系统,自动初始化操纵杆子系统
SDL_INIT_EVERYTHING 初始化所有子系统

上述定义可以只初始化其中一个子系统,也可以多个一起初始化,多个时用|连接。

返回值

初始化成功返回0失败返回一个负数错误代码

用法示例

SDL_Init(SDL_INIT_VIDEO); // 只初始化视频子系统
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); // 初始化视频与音频子系统

// 在初始化的同时进行是否成功判断
if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO) < 0)
{
    SDL_Log("SDL_Init failed: %s", SDL_GetError());
    return -1;
}

 

 

SDL_Quit

声明

// SDL.h
void SDL_Quit(void);

功能

清除(退出)所有已初始化的子系统。

 

 

SDL_CreateWindow

声明

// SDL_video.h
SDL_Window * SDL_CreateWindow(const char *title, int x, int y, int w,int h, Uint32 flags);

功能

参数

参数 含义
title 窗口标题UTF-8编码
x 窗口起始位置x坐标 通常为居中SDL_WINDOWPOS_CENTERED或未定义SDL_WINDOWPOS_UNDEFINED
y 窗口起始位置y坐标 通常为居中SDL_WINDOWPOS_CENTERED或未定义SDL_WINDOWPOS_UNDEFINED
w 窗口宽度
h 窗口高度
flags 窗口显示标志可以是0或者是枚举类型SDL_WindowFlags中多个标志组合

flags可取值为枚举类型SDL_WindowFlags定义在SDL_video.h中多个值时用|连接,一些常用值如下

typedef enum
{
    SDL_WINDOW_FULLSCREEN = 0x00000001,         // 全屏显示
    SDL_WINDOW_OPENGL = 0x00000002,             // 使用OpenGL
    SDL_WINDOW_SHOWN = 0x00000004,              // 显示窗口
    SDL_WINDOW_HIDDEN = 0x00000008,             // 隐藏窗口
    SDL_WINDOW_BORDERLESS = 0x00000010,         // 窗口无边框
    SDL_WINDOW_RESIZABLE = 0x00000020,          // 窗口大小可变
    SDL_WINDOW_MINIMIZED = 0x00000040,          // 窗口最大化
    SDL_WINDOW_MAXIMIZED = 0x00000080,          // 窗口最小化
    SDL_WINDOW_MOUSE_GRABBED = 0x00000100,     
    SDL_WINDOW_INPUT_FOCUS = 0x00000200, 
    SDL_WINDOW_MOUSE_FOCUS = 0x00000400,
    SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ),
    SDL_WINDOW_FOREIGN = 0x00000800, 
    SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000, 
    SDL_WINDOW_MOUSE_CAPTURE    = 0x00004000, 
    SDL_WINDOW_ALWAYS_ON_TOP    = 0x00008000,   // 窗口保持在最顶层
    SDL_WINDOW_SKIP_TASKBAR     = 0x00010000,   
    SDL_WINDOW_UTILITY          = 0x00020000,   
    SDL_WINDOW_TOOLTIP          = 0x00040000,  
    SDL_WINDOW_POPUP_MENU       = 0x00080000,   
    SDL_WINDOW_KEYBOARD_GRABBED = 0x00100000,   //
    SDL_WINDOW_VULKAN           = 0x10000000,   
    SDL_WINDOW_METAL            = 0x20000000,  

    SDL_WINDOW_INPUT_GRABBED = SDL_WINDOW_MOUSE_GRABBED
} SDL_WindowFlags;

返回值

窗口创建成功则返回对应窗口指针失败则返回NULL。

 

 

SDL_DestroyWindow

声明

// SDL_video.h
void SDL_DestroyWindow(SDL_Window * window);

功能

销毁由SDL_CreateWindow创建的窗口。

用法示例

SDL_Window *window = SDL_CreateWindow(
            "SDLWindow", 
            SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 
            WIDTH, HEIGHT, 
            SDL_WINDOW_SHOWN);
if (NULL == window)
{
    SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
    return -1;
}

SDL_Delay(5000);

SDL_DestroyWindow(window);

 

 

SDL_Delay

声明

// SDL_timer.h
void SDL_Delay(Uint32 ms);

功能

让程序暂停(阻塞)指定时间,单位为毫秒。

 

 

SDL_Log

声明

// SDL_log.h
void SDL_Log(const char *fmt, ...)

功能

向控制台输出信息用法与C语言中printf一样。

 

 

SDL_GetError

声明

// SDL_error.h
const char * SDL_GetError(void);

功能

获取最后一次发生错误对应的错误信息。

 

 

SDL_GetWindowSurface

声明

// SDL_video.h
SDL_Surface * SDL_GetWindowSurface(SDL_Window * window);

功能

获取与window关联的SDL_Surface指针。

返回值

成功则返回SDL_Surface指针失败则返回NULL。

 

 

SDL_FreeSurface

声明

// SDL_surface.h
void SDL_FreeSurface(SDL_Surface * surface);

功能

释放SDL_Surface指针。

 

 

SDL_FillRect

声明

// SDL_surface.h
int SDL_FillRect(SDL_Surface * dst, const SDL_Rect * rect, Uint32 color);

功能

在指定的surface上绘制填充一个指定区域的矩形。

参数

参数 含义
dst 需要在上面进行绘制的surface指针
rect 需要绘制的区域
color 4字节无符号整型表示颜色通常用十六进制表示例如0xFF32BACD。每两个字符表示一个通道分别人ARGB通道。但在本函数中透明通道A不会生效所以可以只写RGB三个字节即0x32BACD

区域结构体见SDL_Rect

返回值

填充成功返回0失败返回负数错误代码。

 

 

SDL_UpdateWindowSurface

声明

// SDL_video.h
int SDL_UpdateWindowSurface(SDL_Window * window);

功能

将Surface的数据更新到屏幕上

返回值

更新成功返回0失败返回负数错误代码

 

 

SDL_MapRGB

声明

// SDL_pixels.h
Uint32 SDL_MapRGB(const SDL_PixelFormat * format,Uint8 r, Uint8 g, Uint8 b);

功能

使用指定的像素格式将r, g, b三个通道的颜色分量转为一个颜色值。

参数

参数 含义
format 描述像素格式的结构体
r 红色通道分量值0~255
g 绿色通道分量值, 0~255
b 蓝色通道分量值, 0~255

返回值

由三个分量合并成的像素颜色值。

 

 

SDL_MapRGBA

声明

// SDL_pixels.h
Uint32 SDL_MapRGBA(const SDL_PixelFormat * format,Uint8 r, Uint8 g, Uint8 b, Uint8 a);

功能

使用指定的像素格式将r, g, b, a 四个通道的颜色分量转为一个颜色值。

参数

参数 含义
format 描述像素格式的结构体
r 红色通道分量值0~255
g 绿色通道分量值, 0~255
b 蓝色通道分量值, 0~255
a 透明通道分量值, 0~255

返回值

由三个分量合并成的像素颜色值。

 

 

SDL_LoadBMP

声明

// SDL_surface.h
#define SDL_LoadBMP(file)   SDL_LoadBMP_RW(SDL_RWFromFile(file, "rb"), 1)
// SDL_LoadBMP的声明是一个宏定义其效果等同于下面的写法
SDL_Surface * SDL_LoadBMP_RW(const char * file);

功能

导入BMP图片数据。

参数

file: 文件名

返回值

导入成功返回一个指向SDL_Surface的指针导入失败则返回NULL。

 

 

SDL_BlitSurface

声明

// SDL_surface.h
#define SDL_BlitSurface SDL_UpperBlit

int SDL_UpperBlit(SDL_Surface * src, const SDL_Rect * srcrect,SDL_Surface * dst, SDL_Rect * dstrect);

功能

将一个surface的内容复制到另一个surface当中。

参数

参数 含义
src 源surface
srcrect 源surface需要复制的区域
dst 目标surface
dstrect 目标surface的区域不生效

返回值

复制成功返回0失败返回负数错误码。

 

 

SDL_BlitScaled

声明

// SDL_surface.h
#define SDL_BlitScaled SDL_UpperBlitScaled
int SDL_UpperBlitScaled(SDL_Surface * src, const SDL_Rect * srcrect,SDL_Surface * dst, SDL_Rect * dstrect);

功能

将一个surface的内容复制到另一个surface当中。两个区域大小不一致时进行缩放。

参数

参数 含义
src 源surface
srcrect 源surface需要复制的区域
dst 目标surface
dstrect 目标surface的区域

返回值

复制成功返回0失败返回负数错误码。

 

 

SDL_CreateRGBSurface

SDL_CreateRGBSurfaceWithFormat

SDL_CreateRGBSurfaceFrom

SDL_CreateRGBSurfaceWithFormatFrom

SDL_LockSurface

SDL_UnlockSurface

SDL_CreateRenderer

声明

// SDL_render.h
SDL_Renderer * SDL_CreateRenderer(SDL_Window * window,int index, Uint32 flags);

功能

给指定窗口创建一个2D渲染环境。

参数

参数 含义
window 需要创建渲染环境的窗口指针
index 初始化渲染驱动索引。通常为-1表示自动初始化第一个支持的驱动
flags 0或者SDL_RendererFlags一个或多个值的组合

SDL_RendererFlags为枚举类型,多个值时用|连接。如下

// SDL_render.h
typedef enum
{
    SDL_RENDERER_SOFTWARE = 0x00000001,         // 软件(CPU)渲染
    SDL_RENDERER_ACCELERATED = 0x00000002,      // 硬件加速
    SDL_RENDERER_PRESENTVSYNC = 0x00000004,     
    SDL_RENDERER_TARGETTEXTURE = 0x00000008    
} SDL_RendererFlags;

返回值

成功返回渲染器指针失败返回NULL。

 

 

SDL_DestroyRenderer

声明

// SDL_render.h
void SDL_DestroyRenderer(SDL_Renderer * renderer);

功能

销毁渲染环境并释放关联的texture

 

 

SDL_SetRenderDrawColor

声明

// SDL_render.h
int SDL_SetRenderDrawColor(SDL_Renderer * renderer,Uint8 r, Uint8 g, Uint8 b,Uint8 a);

功能

设置绘制操作(绘制/填充矩形、线条、点以及清除屏幕)的颜色。

参数

参数 含义
render 渲染器
r 红色通道值
g 绿色通道值
b 蓝色通道值
a 透明通道值

返回值

设置成功返回0失败返回负数错误代码。

 

 

SDL_RenderClear

声明

// SDL_render.h
int SDL_RenderClear(SDL_Renderer * renderer);

功能

利用SDL_SetRenderDrawColor设置的颜色清除当前渲染目标。

返回值

清除成功返回0失败返回负数错误码。

 

 

SDL_RenderPresent

声明

// SDL_render.h
void SDL_RenderPresent(SDL_Renderer * renderer);

功能

将渲染操作的数据更新到屏幕上。

 

 

SDL_SetRenderDrawBlendMode

声明

// SDL_render.h
int SDL_SetRenderDrawBlendMode(SDL_Renderer * renderer,SDL_BlendMode blendMode);

功能

为绘制操作设置颜色混合模式。

参数

混合模式为枚举类型SDL_BlendMode定义及常用值如下

// SDL_blendmode.h

typedef enum
{
    SDL_BLENDMODE_NONE = 0x00000000,     // 默认模式。无混合,透明通道不生效
    SDL_BLENDMODE_BLEND = 0x00000001,    // Alpha通道混合透明通道生效

    // 下面这些混合模式暂时不用管, 有兴趣可以自行研究一下
    SDL_BLENDMODE_ADD = 0x00000002,      
    SDL_BLENDMODE_MOD = 0x00000004,     
    SDL_BLENDMODE_MUL = 0x00000008, 
    SDL_BLENDMODE_INVALID = 0x7FFFFFFF
} SDL_BlendMode;

 

 

SDL_RenderDrawPoint

声明

// SDL_render.h
int SDL_RenderDrawPoint(SDL_Renderer * renderer,int x, int y);

功能

根据提供的坐标在当前渲染目标上绘制一个点。

返回值

绘制成功返回0失败返回负值错误码。

 

 

SDL_RenderDrawPoints

声明

// SDL_render.h
int SDL_RenderDrawPoints(SDL_Renderer * renderer,const SDL_Point * points,int count);

功能

在当前渲染环境上绘制多个点。

参数

参数 含义
renderer 当前渲染器指针
points 指向一个SDL_Point数组的指针
count 点的数量

返回值

绘制成功则返回0失败返回负数错误码。

 

 

SDL_RenderDrawLine

声明

// SDL_render.h
int SDL_RenderDrawLine(SDL_Renderer * renderer,int x1, int y1, int x2, int y2);

功能

根据提供的两个点的坐标在当前渲染环境上绘制一条线。

返回值

绘制成功返回0失败返回负数错误码。

 

 

SDL_RenderDrawLines

声明

// SDL_render.h
int SDLCALL SDL_RenderDrawLines(SDL_Renderer * renderer,const SDL_Point * points,int count);

功能

沿着提供的多个点绘制线段。

参数

SDL_RenderDrawPoints参数。

返回值

绘制成功返回0失败返回负数错误码。

 

 

SDL_RenderDrawRect

声明

// SDL_render.h
int SDL_RenderDrawRect(SDL_Renderer * renderer, const SDL_Rect * rect);

功能

在当前渲染目标上指定区域绘制一个矩形。

返回值

绘制成功返回0失败返回负数错误码。

 

 

SDL_RenderDrawRects

声明

// SDL_render.h
int SDL_RenderDrawRects(SDL_Renderer * renderer, const SDL_Rect * rects, int count);

功能

根据提供的区域数组在当前渲染目标上绘制多个矩形。

参数

参数 含义
renderer 渲染器指针
rects 指向SDL_Rect数组的指针
count 区域数量

返回值

绘制成功返回0失败返回负数错误码。

 

 

SDL_RenderFillRect

声明

// SDL_render.h
int SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect);

功能

在当前渲染目标上指定区域填充一个矩形。

返回值

填充成功返回0失败返回负数错误码。

 

 

SDL_RenderFillRects

声明

// SDL_render.h
int SDL_RenderFillRects(SDL_Renderer * renderer, const SDL_Rect * rects, int count);

功能

根据提供的区域数组在当前渲染目标上填充多个矩形。

参数

参数 含义
renderer 渲染器指针
rects 指向SDL_Rect数组的指针
count 区域数量

返回值

填充成功返回0失败返回负数错误码。

 

 

SDL_RenderSetScale

SDL_CreateTextureFromSurface

声明

// SDL_render.h
DL_Texture * SDL_CreateTextureFromSurface(SDL_Renderer * renderer, SDL_Surface * surface);

功能

由已存在的Surface创建Texture。

返回值

创建成功返回对应SDL_Texture指针失败返回NULL。

 

 

SDL_DestroyTexture

声明

// SDL_render.h
void SDL_DestroyTexture(SDL_Texture * texture);

功能

销毁指定的Texture。

 

 

SDL_RenderCopy

声明

// SDL_render.h
int SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_Rect * dstrect);

功能

从Texture中复制指定部分到渲染目标中。当源区域与指定区域大小不一致时会进行相应缩放。

参数

参数 含义
renderer 渲染器指针
texture 需要进行复制的texture
srcrect 需要进行复制的源区域NULL表示整个区域
dstrect 需要进行复制的目标区域NULL表示整个区域

 

 

SDL_RenderCopyEx

声明

// SDL_render.h
int SDLCALL SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,  const SDL_Rect * srcrect, const SDL_Rect * dstrect, const double angle, const SDL_Point *center, const SDL_RendererFlip flip);

功能

从Texture中复制指定部分到渲染目标中 同时可进行旋转和镜像翻转操作。

参数

参数 含义
render 渲染器指针
texture 需要进行复制的Texture
srcrect 需要进行复制的源区域NULL表示整个区域
dstrect 复制去的目标区域NULL表示整个渲染目标区域
angle 旋转角度,顺时针方向旋转
center 旋转中心点为相对dstrect左上角的坐标。NULL表示围绕dstrect中心旋转
flip 镜像翻转。为枚举类型SDL_RendererFlip见下
// SDL_render.h
typedef enum
{
    SDL_FLIP_NONE = 0x00000000,     // 不进行翻转
    SDL_FLIP_HORIZONTAL = 0x00000001,    // 进行水平镜像翻转
    SDL_FLIP_VERTICAL = 0x00000002     // 进行垂直镜像翻转
} SDL_RendererFlip;

 

 

SDL_SetTextureAlphaMod

声明

// SDL_render.h
int SDL_SetTextureAlphaMod(SDL_Texture * texture, Uint8 alpha);

功能

为render copy设置透明度需要配合SDL_SetTextureBlendMode使用。

返回值

设置成功返回0失败返回负数错误码。

 

 

SDL_SetTextureBlendMode

声明

// SDL_render.h
int SDL_SetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode blendMode);

功能

SDL_RenderCopy设置颜色混合模式。

参数

SDL_BlendMode见SDL_SetRenderDrawBlendMode参数。

返回值

成功返回0失败返回负数错误码。

 

 

SDL_UpdateTexture

SDL_UpdateYUVTexture

SDL_UpdateNVTexture

SDL_LockTexture

SDL_LockTextureToSurface

SDL_UnlockTexture

SDL_PointInRect

声明

// SDL_rect.h
SDL_bool SDL_PointInRect(const SDL_Point *p, const SDL_Rect *r);

功能

判断一个点是否在一个区域中。

参数

SDL_Point

SDL_Rect

返回值

SDL_bool

点在区域内返回SDL_TRUE 点不在区域内返回SDL_FALSE。

 

 

SDL_RectEmpty

声明

// SDL_rect.h
SDL_bool SDL_RectEmpty(const SDL_Rect *r);

功能

判断一个区域是否面积。

参数

SDL_Rect

返回值

如果r为NULL或者对应区域面积计算值小于或等于0则返回SDL_TRUE否则返回SDL_FALSE。

 

 

SDL_RectEquals

声明

// SDL_rect.h
SDL_bool SDL_RectEquals(const SDL_Rect *a, const SDL_Rect *b);

功能

判断两个区域是否相同。

参数

SDL_Rect

返回值

如果两个SDL_Rect指针都不为NULL且左上角坐标相同、宽高也相同则返回SDL_TRUE否则返回SDL_FALSE。

 

 

SDL_HasIntersection

声明

// SDL_rect.h
SDL_bool SDL_HasIntersection(const SDL_Rect * A,const SDL_Rect * B);

功能

判断两个矩形区域是否有相交部分。

参数

SDL_Rect

返回值

如果两个SDL_Rect指针都不为NULL且有相交部分则返回SDL_TRUE否则返回SDL_FALSE。

 

 

SDL_IntersectRect

声明

// SDL_rect.h
SDL_bool SDLCALL SDL_IntersectRect(const SDL_Rect * A, const SDL_Rect * B, SDL_Rect * result);

功能

计算两个矩形区域相交部分。

参数

AB 需要计算的两个区域

result A与B相交的部分。

SDL_Rect

返回值

如果A、B有相交部分则返回SDL_TRUE否则返回SDL_FALSE。

 

 

SDL_UnionRect

声明

// SDL_rect.h
void SDLCALL SDL_UnionRect(const SDL_Rect * A, const SDL_Rect * B, SDL_Rect * result);

功能

计算两个矩形区域的并集。如果A、B是两个分离开的区域则并为能将A、B两个区域包含在内的最小矩形区域。

参数

AB 需要计算的两个区域

result A与B并集。

SDL_Rect

 

 

SDL_LoadWAV

声明

// SDL_audio.h

#define SDL_LoadWAV(file, spec, audio_buf, audio_len) \
    SDL_LoadWAV_RW(SDL_RWFromFile(file, "rb"),1, spec,audio_buf,audio_len)

SDL_AudioSpec * SDL_LoadWAV_RW(SDL_RWops * src, int freesrc, SDL_AudioSpec * spec, Uint8 ** audio_buf, Uint32 * audio_len);

功能

导入WAV格式音频文件。

参数

参数 含义
file WAV文件名
spec SDL_AudioSpec音频文件信息
audio_buf 音频文件数据指针
audio_len 音频文件长度(字节)指针

SDL_AudioSpec

返回值

成功返回SDL_AudioSpec指针失败返回NULL。

 

 

SDL_FreeWAV

声明

// SDL_audio.h
void SDL_FreeWAV(Uint8 * audio_buf);

功能

释放SDL_LoadWAV导入的音频数据。

 

 

SDL_OpenAudioDevice

声明

// SDL_audio.h
SDL_AudioDeviceID SDL_OpenAudioDevice(const char *device, int iscapture, const SDL_AudioSpec *desired, SDL_AudioSpec *obtained, int allowed_changes);

功能

打开指定的音频设备。

参数

参数 含义
device 设备名称,由SDL_GetAudioDeviceName获得为NULL则请求能使用的默认设备
iscapture 是否是录音设备0表示播放设备非0表示录音设备
desired 期望输出格式。SDL_AudioSpec
obtained 实际输出格式。SDL_AudioSpec
allowed_changes 当硬件不支持时是否允许改变。通常为0

返回值

成功则返回一个有效设备ID。失败返回0

 

 

SDL_PauseAudioDevice

声明

// SDL_audio.h
void SDL_PauseAudioDevice(SDL_AudioDeviceID dev, int pause_on);

功能

设置指定播放设备的暂停和播放。

参数

devSDL_OpenAudioDevice打开的设备ID。

pause_on 暂停/播放0为播放非0为暂停。

 

 

SDL_GetAudioDeviceName

声明

// SDL_audio.h

const char * SDL_GetAudioDeviceName(int index, int iscapture);

功能

根据索引查询音频设备名称。

参数

index 设备索引

iscapture 是否为录音设备。0查询非录音设备非0查询录音设备。

返回值

查询到设备返回设备名称否则返回NULL。

 

 

结构体、枚举、宏定义

SDL_bool

定义

// SDL_stdinc.h

typedef enum
{
    SDL_FALSE = 0,
    SDL_TRUE = 1
} SDL_bool;

描述

定义布尔值。虽然也是1为真0为假但是这样定义之后在代码中使用这个定义可以增加可读性。

 

 

SDL_Point

定义

// SDL_rect.h
typedef struct SDL_Point
{
    int x; // 点的横坐标
    int y; // 点的纵坐标
} SDL_Point;

描述

定义了一个点。

 

 

SDL_Rect

定义

// SDL_rect.h
typedef struct SDL_Rect
{
    int x, y; //矩形区域左上角坐标
    int w, h; // 矩形区域宽和高 单位为像素
} SDL_Rect;

描述

一个由左上角坐标和宽高决定的矩形区域。

 

 

SDL_Surface

定义

// SDL_surface.h
typedef struct SDL_Surface
{
    Uint32 flags;               //
    SDL_PixelFormat *format;    // 像素格式信息
    int w, h;                   // 宽、高
    int pitch;                  // 每一行像素字节数
    void *pixels;               // 界面上像素对应的数据内存指针


    /** 以下这些成员可以暂时先不用了解 */
    void *userdata;
    int locked;              
    void *list_blitmap;
    SDL_Rect clip_rect;
    SDL_BlitMap *map;
    int refcount; 
} SDL_Surface;

描述

进行位图操作的像素集。可以理解为一张画布,可以在上面进行绘图和其他像素操作。

 

 

SDL_AudioSpec

定义

// SDL_audio.h

typedef struct SDL_AudioSpec
{
    int freq;                   // 采样率
    SDL_AudioFormat format;     // 音频数据格式
    Uint8 channels;             // 声道数: 1为单声道 2为立体声
    Uint8 silence;              // 
    Uint16 samples;             //
    Uint16 padding;             //
    Uint32 size;                // 音频数据大小(字节)
    SDL_AudioCallback callback; // 为音频设备提供数据的回调函数
    void *userdata;             // 向回调函数中传递的数据
} SDL_AudioSpec;

描述

保存音频文件一些格式信息。

userdata最主要用于传递SDL_LoadWAV加载进来的audio_buf数据也可以自定义一些其他数据。

SDL_AudioCallback为回调函数类型定义如下

typedef void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 * stream,int len);

userdata为通过SDL_AudioSpec传入的userdata,

stream为每次向音频设备传递的声音数据

len为传递的stream长度。

一个SDL_AudioCallback例子如下

typedef struct {
    Uint8 *audio_buf;
    Uint32 audio_len;
    Uint32 pos;
} AudioInfo;


void callback(void *userdata, Uint8 * stream, int len)
{
    AudioInfo *audio_info = (AudioInfo *)userdata;

    Uint32 remain_len = audio_info->audio_len - audio_info->pos;
    if (remain_len > len)
    {
        SDL_memcpy(stream, audio_info->audio_buf + audio_info->pos, len);
        audio_info->pos += len;
    }
    else
    {
        SDL_memcpy(stream, audio_info->audio_buf + audio_info->pos, remain_len);
        audio_info->pos = 0;
    }

}

void play_audio()
{
    SDL_AudioSpec spec;
    Uint8 *audio_buf;
    Uint32 audio_len;
    if (SDL_LoadWAV("sound.wav", &spec, &audio_buf, &audio_len) == NULL)
    {
        SDL_Log("wav load failed: %s", SDL_GetError());
        return -1;
    }
    spec.callback = callback;
    AudioInfo audio_info = {audio_buf, audio_len, 0};
    spec.userdata = &audio_info;

    SDL_AudioDeviceID dvice_id = SDL_OpenAudioDevice(NULL, 0, &spec, &spec, 0);

    SDL_PauseAudioDevice(dvice_id, SDL_FALSE);
}

Unknown

#include <SDL2/SDL.h>

#define WIDTH 720
#define HEIGHT 720

#define FR 30
#define FT 1000 / FR


SDL_Window *window;
SDL_Renderer *render;
SDL_Surface *image_surface;
SDL_Texture *image_texture;
SDL_Rect render_rect;


void calc_redner_rect()
{
    int window_width, window_height, texture_width, texture_height;
    SDL_GetWindowSize(window, &window_width, &window_height);
    SDL_QueryTexture(image_texture, NULL, NULL, &texture_width, &texture_height);
    double scale = 1.0;
    if ((texture_width > window_width) && (texture_height > window_height))
    {
        double xscale = (double)window_width / texture_width;
        double yscale = (double)window_height / texture_height;
        scale = xscale < yscale ? xscale : yscale;        
    }
    else if (texture_width > window_width)
    {
        scale = (double)window_width / texture_width;
    }
    else if (texture_height > window_height)
    {
        scale = (double)window_height / texture_height;
    }

    texture_height *= scale;
    texture_width *= scale; 

    render_rect.x = (window_width - texture_width) / 2;
    render_rect.y = (window_height - texture_height) / 2;
    render_rect.w = texture_width;
    render_rect.h = texture_height;


    SDL_Log("calc_redner_rect");
}


int initialize()
{
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
    {
        SDL_Log("SDL_Init failed: %s", SDL_GetError());
        return -1;
    }

    window = SDL_CreateWindow("SDL", 
            SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 
            WIDTH, HEIGHT, 
            SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE
        );
    if (NULL == window)
    {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        return -1;
    }
    render = SDL_CreateRenderer(window, -1, 0);
    if (NULL == render)
    {
        SDL_Log("SDL_CreateRenderer error: %s", SDL_GetError());
        return -1;
    }

    image_surface = SDL_LoadBMP("1685074549643.bmp");
    if (NULL == image_surface)
    {
        SDL_Log("Image load failed: %s", SDL_GetError());
        return -1;
    }

    image_texture = SDL_CreateTextureFromSurface(render, image_surface);
    if (NULL == image_texture)
    {
        SDL_Log("SDL_CreateTextureFromSurface failed: %s", SDL_GetError());
        return -1;
    }

    calc_redner_rect();

    return 0;
}

void release()
{
    if (NULL != image_texture)
    {
        SDL_DestroyTexture(image_texture);
    }

    if (NULL != image_surface)
    {
        SDL_FreeSurface(image_surface);
    }

    if (NULL != render)
    {
        SDL_DestroyRenderer(render);
    }

    if (NULL != window)
    {
        SDL_DestroyWindow(window);
    }    

    SDL_Quit();
}


void draw()
{
    SDL_SetRenderDrawColor(render, 255, 255, 255, 255);
    SDL_RenderClear(render);

    SDL_RenderCopy(render, image_texture, NULL, &render_rect);
    SDL_RenderPresent(render);
}




void event_loop()
{
    SDL_Event event;
    while (1)
    {
        Uint64 start = SDL_GetTicks64();
        while (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
            case SDL_MOUSEMOTION:
                break;
            case SDL_MOUSEBUTTONDOWN:
                break;
            case SDL_MOUSEBUTTONUP:
                break;
            case SDL_MOUSEWHEEL:
                break;
            case SDL_QUIT:
                return;
            case SDL_WINDOWEVENT:
                if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
                {
                    calc_redner_rect();
                }
                break;
            default:
                break;
            }
        }
        draw();
        Uint64 end = SDL_GetTicks64();
        int time = end - start;
        // SDL_Log("time = %d, FT = %d", time, FT);
        if (time < FT)
        {
            SDL_Delay(FT - time);
        }
    }

}


typedef struct {
    Uint8 *audio_buf;
    Uint32 audio_len;
    Uint32 pos;
} AudioInfo;

void callback(void *userdata, Uint8 * stream, int len)
{
    AudioInfo *audio_info = (AudioInfo *)userdata;
    // SDL_Log("len = %d, len = %d, pos = %u", audio_info->audio_len, len, audio_info->pos);
    Uint32 remain_len = audio_info->audio_len - audio_info->pos;
    if (remain_len > len)
    {
        SDL_memcpy(stream, audio_info->audio_buf + audio_info->pos, len);
        audio_info->pos += len;
    }
    else
    {
        SDL_memcpy(stream, audio_info->audio_buf + audio_info->pos, remain_len);
        audio_info->pos = 0;
    }

}


void play_wav()
{

}

#undef main
int main(int argc, char *argv[])
{
    if (initialize() < 0)
    {
        release();
        return -1;
    }
    SDL_AudioSpec spec;
    Uint8 *audio_buf;
    Uint32 audio_len;
    if (SDL_LoadWAV("sound.wav", &spec, &audio_buf, &audio_len) == NULL)
    {
        SDL_Log("wav load failed: %s", SDL_GetError());
        return -1;
    }
    spec.callback = callback;
    AudioInfo audio_info = {audio_buf, audio_len, 0};
    spec.userdata = &audio_info;
    SDL_Log("%u", audio_len);
    SDL_AudioDeviceID dvice_id = SDL_OpenAudioDevice(NULL, 0, &spec, &spec, 0);

    SDL_PauseAudioDevice(dvice_id, SDL_FALSE);
    SDL_Log("Here !");
    event_loop();
    SDL_CloseAudioDevice(dvice_id);
    SDL_FreeWAV(audio_buf);
    release();
    return 0;
}

CPP多线程