SDL简单教程
第二话: 窗口和表面(Surface)
前言
SDL2(Simple DirectMedia Layer 2)是一个跨平台的多媒体库。它提供了对音频、键盘、鼠标、游戏控制器和图形硬件(通过 OpenGL、Vulkan 等)的低级访问接口,主要用于开发游戏和其他交互式多媒体应用程序。本章介绍创建窗口以及获取窗口表面信息。
第二话: 窗口和表面(Surface)
2.1 创建窗口
SDL_CreateWindow
函数全面解析- 函数原型:
SDL_Window* SDL_CreateWindow(const char* title, int x, int y, int w, int h, Uint32 flags);
- 参数详细说明:
title
:一个以空字符结尾的字符串,用于指定窗口的标题。这个标题会显示在窗口的标题栏上,例如可以是游戏名称或者应用程序名称。例如"My SDL2 Application"
。x
和y
:这两个参数用于指定窗口的初始位置。有以下几种特殊值可以使用:SDL_WINDOWPOS_UNDEFINED
:窗口的位置由操作系统决定,通常会根据当前的桌面布局和系统设置自动放置窗口。SDL_WINDOWPOS_CENTERED
:将窗口在屏幕上居中显示。如果是多显示器环境,会在主显示器上居中。SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex)
:将窗口在指定索引的显示器上居中显示。displayIndex
是显示器的索引,从0开始,可以通过SDL_GetNumVideoDisplays
函数获取显示器数量,并结合SDL_GetDisplayBounds
函数来确定显示器的边界和索引信息。例如,要在第二个显示器上居中显示窗口,可以使用SDL_WINDOWPOS_CENTERED_DISPLAY(1)
(假设显示器索引从0开始)。
w
和h
:分别表示窗口的宽度和高度,以像素为单位。可以根据应用程序的需求设置合适的大小,比如对于一个简单的游戏窗口,可以设置为800
和600
像素。flags
:一个或多个标志位的组合(使用按位或操作符|
),用于指定窗口的属性,常见的标志位如下:SDL_WINDOW_SHOWN
:创建窗口后立即显示窗口。如果不使用这个标志,窗口创建后将是隐藏状态,需要通过其他方式来显示。SDL_WINDOW_RESIZABLE
:使窗口可调整大小。用户可以通过拖动窗口边框来改变窗口的大小,这在需要适应不同屏幕分辨率或者用户自定义窗口大小时很有用。在程序中,需要处理SDL_WINDOWEVENT_RESIZED
事件来响应窗口大小的改变。SDL_WINDOW_MAXIMIZED
:创建窗口时将其最大化显示。这会使窗口占据整个屏幕或者充满其所在的显示器(在多显示器环境下)。SDL_WINDOW_MINIMIZED
:创建窗口时将其最小化显示,窗口会在任务栏或者系统的最小化区域显示,用户可以通过点击任务栏图标来恢复窗口。SDL_WINDOW_BORDERLESS
:创建一个无边框的窗口。这种窗口常用于创建全屏或者特殊样式的应用程序,比如一些游戏的全屏模式或者特定的图形界面设计。SDL_WINDOW_FULLSCREEN
:将窗口设置为全屏模式。这会隐藏桌面和其他窗口,使应用程序占据整个屏幕。注意,在全屏模式下,需要考虑不同显示器的分辨率和刷新率等因素,可能需要进行额外的设置和处理。SDL_WINDOW_FULLSCREEN_DESKTOP
:一种特殊的全屏模式,它会以桌面的分辨率来显示窗口,并且可能保留一些桌面元素(如任务栏),具体行为可能因操作系统而异。
- 返回值处理:
SDL_CreateWindow
函数返回一个指向SDL_Window
类型的指针。如果窗口创建成功,这个指针将指向新创建的窗口对象,可以用于后续对窗口的操作,如获取窗口表面、设置窗口标题等。如果返回值为nullptr
,则表示窗口创建失败,此时应该调用SDL_GetError
函数获取错误信息。以下是一个创建可调整大小、初始位置居中且带有标题的窗口的示例代码:
- 函数原型:
SDL_Window* window = SDL_CreateWindow("My SDL2 Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if (window == nullptr)
{
std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << std::endl;
return 1;
}
- 窗口属性的获取和修改函数
SDL_GetWindowFlags
函数:用于获取窗口的当前标志位。它接受一个SDL_Window*
类型的参数(即窗口指针),并返回一个Uint32
类型的值,表示窗口的标志位。例如:
Uint32 flags = SDL_GetWindowFlags(window);
if (flags & SDL_WINDOW_RESIZABLE)
{
std::cout << "The window is resizable." << std::endl;
}
- **`SDL_SetWindowTitle`函数**:用于修改窗口的标题。它接受窗口指针和新的标题字符串作为参数。例如,如果要在程序运行过程中改变窗口标题:
SDL_SetWindowTitle(window, "New Window Title");
- **`SDL_SetWindowSize`和`SDL_GetWindowSize`函数**:`SDL_SetWindowSize`函数用于设置窗口的大小,接受窗口指针、新的宽度和高度作为参数。`SDL_GetWindowSize`函数用于获取窗口当前的大小,接受窗口指针和两个指向`int`类型的指针(用于存储宽度和高度)作为参数。示例如下:
// 设置窗口大小
SDL_SetWindowSize(window, 800, 600);
int width, height;
// 获取窗口大小
SDL_GetWindowSize(window, &width, &height);
std::cout << "Current window size: width = " << width << ", height = " << height << std::endl;
- **`SDL_SetWindowPosition`和`SDL_GetWindowPosition`函数**:`SDL_SetWindowPosition`函数用于设置窗口的位置,接受窗口指针、新的`x`和`y`坐标作为参数。`SDL_GetWindowPosition`函数用于获取窗口当前的位置,接受窗口指针和两个指向`int`类型的指针(用于存储`x`和`y`坐标)作为参数。例如:
// 设置窗口位置
SDL_SetWindowPosition(window, 100, 100);
int x, y;
// 获取窗口位置
SDL_GetWindowPosition(window, &x, &y);
std::cout << "Current window position: x = " << x << ", y = " << y << std::endl;
2.2 获取窗口表面(Surface)
SDL_GetWindowSurface
函数深入理解- 函数原型:
SDL_Surface* SDL_GetWindowSurface(SDL_Window* window);
- 参数含义:
window
参数是之前创建的SDL_Window
类型的指针,即要获取表面的窗口对象。 - 返回值分析:该函数返回一个指向
SDL_Surface
类型的指针。SDL_Surface
结构体用于表示一个图像表面,它包含了图像的像素数据、像素格式、宽度、高度等信息。如果函数执行成功,返回的指针指向窗口对应的表面,可以在这个表面上进行绘制操作。如果返回nullptr
,表示获取窗口表面失败,需要检查窗口是否有效以及是否有其他错误情况,可以通过SDL_GetError
函数获取错误信息。以下是一个获取窗口表面的示例:
- 函数原型:
SDL_Surface* screenSurface = SDL_GetWindowSurface(window);
if (screenSurface == nullptr)
{
std::cerr << "SDL_GetWindowSurface failed: " << SDL_GetError() << std::endl;
return 1;
}
SDL_Surface
结构体成员介绍pixels
成员:这是一个指向表面像素数据的指针。它存储了图像的实际像素值,根据表面的像素格式不同,这些像素值的存储方式和解读方式也不同。例如,对于24位真彩色格式(RGB,每个通道8位),pixels
指向的内存区域中,每个像素由3个连续的字节表示(分别代表红、绿、蓝通道)。pitch
成员:表示一行像素数据的字节长度。在某些情况下,为了内存对齐等原因,pitch
的值可能大于图像宽度乘以每个像素的字节数。例如,在一个宽度为320像素的24位真彩色表面中,理论上一行像素数据应该是320 * 3 = 960字节,但实际上pitch
可能是1024字节(内存对齐到1024字节边界)。w
和h
成员:分别表示表面的宽度和高度,以像素为单位。这两个值确定了表面的尺寸,可以用于计算表面中像素的总数或者进行遍历操作。format
成员:这是一个SDL_PixelFormat
结构体,用于描述表面的像素格式。它包含了像素的格式类型(如SDL_PIXELFORMAT_RGB24
表示24位真彩色)、每个通道的位数、字节顺序等信息。可以通过这个结构体来了解如何正确地访问和处理表面的像素数据。例如,通过format->BitsPerPixel
可以获取每个像素的位数。
2.3 在表面上简单绘制
SDL_FillRect
函数详解- 函数原型:
int SDL_FillRect(SDL_Surface* dst, const SDL_Rect* rect, Uint32 color);
- 参数剖析:
dst
:指向目标SDL_Surface
的指针,即要在哪个表面上进行绘制。这个表面通常是通过SDL_GetWindowSurface
函数获取的窗口表面。rect
:一个指向SDL_Rect
结构体的指针,用于指定要填充的矩形区域。SDL_Rect
结构体包含了x
、y
、w
和h
四个成员,分别表示矩形的左上角x
坐标、左上角y
坐标、宽度和高度。如果这个指针为nullptr
,则表示填充整个表面。例如,要填充一个从(10, 10)
开始,宽度为100
,高度为50
的矩形区域,可以这样定义SDL_Rect
结构体:
- 函数原型:
SDL_Rect fillRect = {10, 10, 100, 50};
- **`color`**:要填充的颜色值。这个颜色值需要根据表面的像素格式进行转换。可以使用`SDL_MapRGB`或`SDL_MapRGBA`函数(如果表面支持透明度)来将指定的RGB或RGBA颜色值转换为适合表面像素格式的颜色值。
- **返回值处理**:函数返回0表示填充成功,返回 - 1表示失败。如果失败,可以通过`SDL_GetError`函数获取错误信息。以下是一些使用`SDL_FillRect`函数的示例:
// 填充整个窗口表面为白色(假设窗口表面的像素格式是24位真彩色)
SDL_Rect fillRect = {0, 0, screenSurface->w, screenSurface->h};
Uint32 whiteColor = SDL_MapRGB(screenSurface->format, 255, 255, 255);
if (SDL_FillRect(screenSurface, &fillRect, whiteColor) < 0)
{
std::cerr << "SDL_FillRect failed: " << SDL_GetError() << std::endl;
return 1;
}
// 填充一个指定的矩形区域为红色
SDL_Rect redRect = {50, 50, 200, 100};
Uint32 redColor = SDL_MapRGB(screenSurface->format, 255, 0, 0);
if (SDL_FillRect(screenSurface, &redRect, redColor) < 0)
{
std::cerr << "SDL_FillRect failed: " << SDL_GetError() << std::endl;
return 1;
}
SDL_MapRGB
和SDL_MapRGBA
函数的使用SDL_MapRGB
函数:- 函数原型:
Uint32 SDL_MapRGB(const SDL_PixelFormat* format, Uint8 r, Uint8 g, Uint8 b);
- 参数说明:
format
是目标表面的像素格式结构体指针,可以从SDL_Surface
的format
成员获取。r
、g
、b
分别是要转换的红色、绿色和蓝色通道的值,范围是0 - 255。这个函数根据给定的像素格式将RGB颜色值转换为适合该表面的颜色值(一个Uint32
类型的值)。例如,要将RGB值为(128, 0, 255)
的颜色转换为适合当前表面格式的颜色:
- 函数原型:
Uint32 purpleColor = SDL_MapRGB(screenSurface->format, 128, 0, 255);
- **`SDL_MapRGBA`函数**:
- **函数原型**:`Uint32 SDL_MapRGBA(const SDL_PixelFormat* format, Uint8 r, Uint8 g, Uint8 b, Uint8 a);`
- **参数说明**:和`SDL_MapRGB`类似,但多了一个`a`参数,用于指定透明度,范围也是0 - 255。0表示完全透明,255表示完全不透明。这个函数用于将RGBA颜色值转换为适合表面像素格式的颜色值,适用于支持透明度的表面。例如,要创建一个半透明的蓝色(透明度为128):
Uint32 semiTransparentBlue = SDL_MapRGBA(screenSurface->format, 0, 0, 255, 128);
SDL_UpdateWindowSurface
函数的重要性- 函数原型:
int SDL_UpdateWindowSurface(SDL_Window* window);
- 功能描述:这个函数用于将在窗口表面上进行的绘制操作更新到实际的窗口显示上。当使用
SDL_FillRect
等函数在窗口表面绘制后,这些绘制只是在内存中的表面数据上进行了修改,并没有立即显示在窗口中。SDL_UpdateWindowSurface
函数会将表面数据复制到窗口的显示缓冲区,从而使绘制的内容显示出来。如果函数返回 - 1,表示更新失败,可以通过SDL_GetError
函数获取错误信息。以下是一个完整的示例,包括创建窗口、获取表面、绘制和更新显示:
- 函数原型:
// 注意如果用visual studio的话是#include <SDL.h>
#include <SDL2/SDL.h>
#include <iostream>
int main(int argc, char* argv[])
{
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl;
return 1;
}
SDL_Window* window = SDL_CreateWindow("My SDL2 Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN);
if (window == nullptr)
{
std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
SDL_Surface* screenSurface = SDL_GetWindowSurface(window);
if (screenSurface == nullptr)
{
std::cerr << "SDL_GetWindowSurface failed: " << SDL_GetError() << std::endl;
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
// 填充整个窗口表面为白色
SDL_Rect fillRect = {0, 0, screenSurface->w, screenSurface->h};
Uint32 whiteColor = SDL_MapRGB(screenSurface->format, 255, 255, 255);
if (SDL_FillRect(screenSurface, &fillRect, whiteColor) < 0)
{
std::cerr << "SDL_FillRect failed: " << SDL_GetError() << std::endl;
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
if (SDL_UpdateWindowSurface(window) < 0)
{
std::cerr << "SDL_UpdateWindowSurface failed: " << SDL_GetError() << std::endl;
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
SDL_Delay(3000); // 暂停3秒,以便观察窗口
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}