当在屏幕上渲染多个图像时,通常需要让图像具有透明背景。幸运的是,SDL提供了一种使用颜色键控的简单方法来实现这一点。
//纹理包装类
class LTexture
{
public:
//初始化
LTexture();
//释放
~LTexture();
//在指定路径上加载图像
bool loadFromFile( std::string path );
//释放纹理
void free();
//渲染指定点的纹理
void render( int x, int y );
//获取图像尺寸
int getWidth();
int getHeight();
private:
//实际的硬件纹理
SDL_Texture* mTexture;
//图像尺寸
int mWidth;
int mHeight;
};
在本教程中,我们将把SDL_Texture包装在一个类中,以使一些事情变得更简单。例如,如果你想获得某些关于纹理的信息,如它的宽度或高度,你将不得不使用一些SDL函数来查询纹理的信息。相反,我们要做的是使用一个类来封装和存储纹理的信息。
从设计上来说,这是一个相当直接的类。它有一个构造函数/析构函数对,一个文件加载器,一个deallocator,一个接收位置的渲染器,以及获取纹理尺寸的函数。对于成员变量,它有我们要包裹的纹理,以及存储宽度/高度的变量。
//我们要渲染的窗口
SDL_Window* gWindow = NULL;
//窗口渲染器
SDL_Renderer* gRenderer = NULL;
//场景纹理
LTexture gFooTexture;
LTexture gBackgroundTexture;
对于此场景,我们将在此处加载两个纹理,它们分别声明为“ gFooTexture”和“ gBackgroundTexture”。我们将采用这种foo纹理:
对青色(浅蓝色)背景设置颜色键,并将其渲染在此背景之上:
LTexture::LTexture()
{
//Initialize
mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
LTexture::~LTexture()
{
//Deallocate
free();
}
构造函数初始化变量,而析构函数调用deallocator,这一点我们后面会讲到。
bool LTexture::loadFromFile( std::string path ){
//释放现有的纹理
free();
//最终纹理
SDL_Texture* newTexture = NULL;
//在指定路径加载图像
SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
if( loadedSurface == NULL )
{
printf( "Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError() );
}
else
{
//Color key image
SDL_SetColorKey( loadedSurface, SDL_TRUE, SDL_MapRGB( loadedSurface->format, 0, 0xFF, 0xFF ) );
//用表面像素创建纹理
newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface );
if( newTexture == NULL )
{
printf( "Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
}
else
{
//获取图片尺寸
mWidth = loadedSurface->w;
mHeight = loadedSurface->h;
}
//释放旧的表面
SDL_FreeSurface( loadedSurface );
}
//Return success
mTexture = newTexture;
return mTexture != NULL;
}
纹理加载功能的工作原理和之前的纹理加载课程中的差不多,但做了一些小的但重要的调整。首先,我们对纹理进行重新分配,以防有一个已经加载的纹理。
接下来,在创建纹理之前,我们使用 SDL_SetColorKey[1] 对图像进行颜色抠像。第一个参数是我们要进行颜色抠像的表面,第二个参数涵盖了我们是否要启用颜色抠像,最后一个参数是我们要进行颜色抠像的像素。
从RGB颜色创建像素最跨平台的方法是用SDL_MapRGB[2]。第一个参数是我们想要的像素的格式。幸运的是,加载的表面有一个格式成员变量。最后三个变量是你想要映射的颜色的红、绿、蓝三个组件。这里我们要映射的是青色,也就是红色0,绿色255,蓝色255。
在对加载的表面进行颜色键控后,我们从加载和颜色键控的表面创建一个纹理。如果纹理创建成功,我们存储纹理的宽度/高度,并返回纹理是否加载成功。
void LTexture::free(){
//Free texture if it exists
if( mTexture != NULL )
{
SDL_DestroyTexture( mTexture );
mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
}
deallocator简单地检查纹理是否存在,销毁它,并重新初始化成员变量。
void LTexture::render( int x, int y ){
//设置渲染空间并渲染到屏幕上
SDL_Rect renderQuad = { x, y, mWidth, mHeight };
SDL_RenderCopy( gRenderer, mTexture, NULL, &renderQuad );
}
在这里你可以看到为什么我们需要一个包装类。到目前为止,我们一直在渲染全屏图像,所以我们不需要指定位置。因为我们不需要指定位置,所以我们只需要调用SDL_RenderCopy,最后两个参数为NULL。
当渲染某个地方的纹理时,你需要指定一个目标矩形,设置x/y位置和宽度/高度。在不知道原始图像的尺寸的情况下,我们无法指定宽度/高度。所以这里当我们渲染纹理时,我们用位置参数和成员宽度/高度创建一个矩形,并将这个矩形传入SDL_RenderCopy。
int LTexture::getWidth(){
return mWidth;
}
int LTexture::getHeight(){
return mHeight;
}
最后这些成员函数允许我们在需要的时候获得宽度/高度。
bool loadMedia(){
//Loading success flag
bool success = true;
//Load Foo' texture
if( !gFooTexture.loadFromFile( "10_color_keying/foo.png" ) )
{
printf( "Failed to load Foo' texture image!\n" );
success = false;
}
//Load background texture
if( !gBackgroundTexture.loadFromFile( "10_color_keying/background.png" ) )
{
printf( "Failed to load background texture image!\n" );
success = false;
}
return success;
}
下面是图片加载功能的操作。
void close(){
//Free loaded images
gFooTexture.free();
gBackgroundTexture.free();
//Destroy window
SDL_DestroyRenderer( gRenderer );
SDL_DestroyWindow( gWindow );
gWindow = NULL;
gRenderer = NULL;
//Quit SDL subsystems
IMG_Quit();
SDL_Quit();
}
而这里是deallocators。
//While application is running
while( !quit )
{
//Handle events on queue
while( SDL_PollEvent( &e ) != 0 )
{
//User requests quit
if( e.type == SDL_QUIT )
{
quit = true;
}
}
//Clear screen
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderClear( gRenderer );
//Render background texture to screen
gBackgroundTexture.render( 0, 0 );
//Render Foo' to the screen
gFooTexture.render( 240, 190 );
//Update screen
SDL_RenderPresent( gRenderer );
}
这里是我们纹理渲染的主循环。这是一个基本的循环,它处理事件,清除屏幕,渲染背景,渲染它上面的简笔画,并更新屏幕。
需要注意的是,当你每一帧向屏幕渲染多个东西时,顺序很重要。如果我们先渲染简笔画,背景就会渲染在它上面,你就看不到简笔画了。
在 这里[3]下载本教程的媒体和源代码。
[1]
SDL_SetColorKey: https://wiki.libsdl.org/SDL_SetColorKey
[2]
SDL_MapRGB: https://wiki.libsdl.org/SDL_MapRGB
[3]
这里: http://www.lazyfoo.net/tutorials/SDL/10_color_keying/10_color_keying.zip
[4]
原文链接: http://www.lazyfoo.net/tutorials/SDL/10_color_keying/index.php