一、前言
在这里,我们先介绍优化加载表面,然后将使用 SDL_image 扩展库来加载 png 图像。
二、优化加载表面
1. 问题
加载位图时,它通常以 24 位格式加载,因为大多数位图都是 24 位。默认情况下,大多数(如果不是全部)现代显示器不是 24 位。如果我们对图像进行 位块传输 这是 24 位到 32 位图像,SDL 每次对图像进行分组传输时都会将其转换。
2. 优化
因此,当加载图像时,我们要做的是将其转换为与屏幕相同的格式,因此无需在 blit 上进行转换。这可以通过 SDL_ConvertSurface
轻松完成。我们所要做的就是传入我们要用屏幕格式转换的表面。
请务必注意,SDL_ConvertSurface
会以新格式返回原始副本。此调用后,原始加载的图像仍在内存中。这意味着我们必须 释放原始加载的图面,否则内存中将有同一图像的两个副本。
加载并转换图像后,我们返回最终优化的图像。
3. 示例代码
/**
* @brief 加载单个图像
* @param path:资源路径
*/
SDL_Surface* LoadSurface(const char* path)
{
// 最终优化的资源
SDL_Surface* optimizedSurface = nullptr;
SDL_Surface* loadedSurface = SDL_LoadBMP(path);
if (!loadedSurface)
std::cout << "[Error]: Unable to load image " << path << " SDL Error: " << SDL_GetError() << std::endl;
else
{
optimizedSurface = SDL_ConvertSurface(loadedSurface, gWindowSurface->format, 0);
if (!optimizedSurface)
std::cout << "[Error]: Unable to optimize image " << path << " SDL_Error: " << SDL_GetError() << std::endl;
// 释放未优化的表面
SDL_FreeSurface(loadedSurface);
}
// 返回最终优化的资源
return optimizedSurface;
}
三、SDL_image 扩展库
配置方式和配置 SDL 基本类似,下面是官网链接,都在 Github 上可以找到
记得将动态库放在可执行文件目录或项目目录下
依赖项跟配置 SDL 一样
四、加载 PNG 图片
现在我们导入了 SDL_image 库,接下来就可以加载 PNG 图片了
本节使用素材
这里我还是将链接库卸载源码中
// 链接库
#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "SDL2main.lib")
#pragma comment(lib, "SDL2_image.lib")
这里还需要 初始化 PNG 加载,使用 IMG_Init,它会返回所有当前初始化的标志。
下面用到了 IMG_Init(imgFlags) & imgFlags
,其实就是判断是否相等,也可以使用其他方法,这里是看个人习惯
之后注意顺序改变,因为优化加载表面时需要窗口关联的表面,所以需要先获取窗口表面,再加载资源
// 初始化 PNG 加载
int imgFlags = IMG_INIT_PNG;
if (!(IMG_Init(imgFlags) & imgFlags))
{
std::cout << "[Error]: SDL_image could not initialize! SDL_image Error: " << IMG_GetError() << std::endl;
return false;
}
else
{
// 获取窗口关联的表面
gWindowSurface = SDL_GetWindowSurface(gWindow);
if (!gWindowSurface)
return false;
else
{
// 加载媒体资源
if (!LoadMedia())
{
std::cout << "[Error]: Failed to load Media!" << std::endl;
return false;
}
}
}
代码如下:
/**
* @brief 启动 SDL 并创建窗口
*/
bool Init()
{
// 初始化 SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
std::cout << "[Error]: SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
return false;
}
else
{
// 创建窗口
gWindow = SDL_CreateWindow("HelloSDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (!gWindow)
{
std::cout << "[Error]: Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
}
else
{
// 初始化 PNG 加载
int imgFlags = IMG_INIT_PNG;
if (!(IMG_Init(imgFlags) & imgFlags))
{
std::cout << "[Error]: SDL_image could not initialize! SDL_image Error: " << IMG_GetError() << std::endl;
return false;
}
else
{
// 获取窗口关联的表面
gWindowSurface = SDL_GetWindowSurface(gWindow);
if (!gWindowSurface)
return false;
else
{
// 加载媒体资源
if (!LoadMedia())
{
std::cout << "[Error]: Failed to load Media!" << std::endl;
return false;
}
}
}
}
}
return true;
}
在 LoadSurface 函数中,使用 IMG_Load
SDL_Surface* loadedSurface = IMG_Load(path);
1. 示例代码
/* 此源代码版权归 AnnihilateSword (2022-*)所有,未经书面许可不得转载。*/
// 使用 SDL 和 iostream
#include <SDL.h>
#include <SDL_image.h>
#include <iostream>
// 链接库
#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "SDL2main.lib")
#pragma comment(lib, "SDL2_image.lib")
// 屏幕尺寸常量
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
bool Init(); // 启动 SDL 并创建窗口
bool LoadMedia(); // 加载媒体
SDL_Surface* LoadSurface(const char* path); // 加载单个图像
void Close(); // 清理资源,关闭窗口以及SDL
SDL_Window* gWindow = nullptr; // 主窗口
SDL_Surface* gWindowSurface = nullptr; // 主窗口关联的表面
SDL_Surface* gTexture = nullptr; // 需要加载的纹理(表面)
// 控制纹理坐标
int gTexturePosX = 100; // 纹理 X 坐标
int gTexturePosY = 100; // 纹理 Y 坐标
int gTextureMovementSpeed = 20; // 纹理移动速度
int main(int argc, char* argv[]) // 必须要填写此参数,不然会出现链接错误
{
// 初始化 SDL
if (Init())
{
// 窗口循环
SDL_Event e;
bool quit = false;
while (quit == false)
{
// 处理事件
while (SDL_PollEvent(&e))
{
if (e.type == SDL_QUIT)
quit = true;
else if (e.type == SDL_KEYDOWN) // 处理按键按下事件
{
switch (e.key.keysym.sym)
{ // 按键移动纹理坐标位置
case SDLK_w: gTexturePosY -= gTextureMovementSpeed; break;
case SDLK_s: gTexturePosY += gTextureMovementSpeed; break;
case SDLK_a: gTexturePosX -= gTextureMovementSpeed; break;
case SDLK_d: gTexturePosX += gTextureMovementSpeed; break;
}
}
}
// --- rendering -----------------------------------------------------------------------
// 将表面填充为蓝色
SDL_FillRect(gWindowSurface, nullptr, SDL_MapRGB(gWindowSurface->format, 51, 76, 204));
// 应用纹理
SDL_Rect dstRect{ gTexturePosX, gTexturePosY, 256, 256 };
// SDL_BlitSurface(gTexture, nullptr, gWindowSurface, &dstRect); // 不缩放
SDL_BlitScaled(gTexture, nullptr, gWindowSurface, &dstRect);
// 更新表面
SDL_UpdateWindowSurface(gWindow);
}
}
// 清理资源
Close();
return 0;
}
/**
* @brief 启动 SDL 并创建窗口
*/
bool Init()
{
// 初始化 SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
std::cout << "[Error]: SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
return false;
}
else
{
// 创建窗口
gWindow = SDL_CreateWindow("HelloSDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (!gWindow)
{
std::cout << "[Error]: Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
}
else
{
// 初始化 PNG 加载
int imgFlags = IMG_INIT_PNG;
if (!(IMG_Init(imgFlags) & imgFlags))
{
std::cout << "[Error]: SDL_image could not initialize! SDL_image Error: " << IMG_GetError() << std::endl;
return false;
}
else
{
// 获取窗口关联的表面
gWindowSurface = SDL_GetWindowSurface(gWindow);
if (!gWindowSurface)
return false;
else
{
// 加载媒体资源
if (!LoadMedia())
{
std::cout << "[Error]: Failed to load Media!" << std::endl;
return false;
}
}
}
}
}
return true;
}
/**
* @brief 加载媒体
*/
bool LoadMedia()
{
gTexture = LoadSurface(R"(res\textures\awesomeface.png)");
if (!gTexture)
{
std::cout << "[Error]: Failed to load texture!" << std::endl;
return false;
}
return true;
}
/**
* @brief 加载单个图像
* @param path:资源路径
*/
SDL_Surface* LoadSurface(const char* path)
{
// 最终优化的资源
SDL_Surface* optimizedSurface = nullptr;
SDL_Surface* loadedSurface = IMG_Load(path);
if (!loadedSurface)
std::cout << "[Error]: Unable to load image " << path << " SDL Error: " << SDL_GetError() << std::endl;
else
{
optimizedSurface = SDL_ConvertSurface(loadedSurface, gWindowSurface->format, 0);
if (!optimizedSurface)
std::cout << "[Error]: Unable to optimize image " << path << " SDL_Error: " << SDL_GetError() << std::endl;
// 释放未优化的表面
SDL_FreeSurface(loadedSurface);
}
// 返回最终优化的资源
return optimizedSurface;
}
/**
* @brief 清理资源,关闭窗口以及SDL
*/
void Close()
{
if (gTexture)
{
// 清理纹理资源
SDL_FreeSurface(gTexture);
gTexture = nullptr;
}
// 销毁窗口
SDL_DestroyWindow(gWindow);
// 退出 IMG 和 SDL
IMG_Quit();
SDL_Quit();
}
注意 IMG_Quit()
✨参考文档
这应该是释放所有其他资源后在SDL_image中调用的最后一个函数。这将卸载它用于各种编解码器的任何共享库。
2. 运行结果
但是有没有注意到,这张图片其实是有透明度的,本卷并未讲到如何去加载透明图片,考虑到篇幅,打算作为一个单独的模块介绍加载透明图片的问题
本节内容就到这里了,下卷会继续分享 SDL 的基本使用
The End.