(译)SDL编程入门(10)Color Key
Color Key
当在屏幕上渲染多个图像时,通常需要让图像具有透明背景。幸运的是,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: %sn", 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: %sn", 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
- 【Keras】完整实现‘交通标志’分类、‘票据’分类两个项目,让你掌握深度学习图像分类
- PaddlePaddle发布新版API,简化深度学习编程
- 13(01)总结StringBuffer,StringBuilder,数组高级,Arrays,Integer,Character
- thymeleaf模板引擎调用java类中的方法(附源码)
- 由hugepage设置导致的数据库事故(r4笔记第28天)
- 判断js引擎是javascriptCore或者v8
- Springboot系列:Springboot与Thymeleaf模板引擎整合基础教程(附源码)
- 同样的sql执行结果不同的原因分析 (r4笔记第27天)
- 情感分析的新方法,使用word2vec对微博文本进行情感分析和分类
- 垂直属性
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(二)RESTful API实战笔记(接口设计及Java后端实现)
- 13(02)总结StringBuffer,StringBuilder,数组高级,Arrays,Integer,Character
- Mybatis-Generator生成Mapper文件中<if test="criteria.valid">的问题解答
- Java开源博客My-Blog之mysql容器重复初始化的严重bug修复过程
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 查找算法笔记(C++版)
- C++基础 指针使用注意
- FreeRTOS移植-基于STM32F407
- 感知机的股票预测算例及python代码实现 | 山人聊算法 | 5th
- C++基础 智能指针
- 排序算法笔记(C++版)
- 算法集锦(17) | 推荐系统 | 基于机器学习的商品定价系统
- C++基础 STL简介
- 基于层次聚类的工业数据分析研究
- 详细记录了python爬取小说《元尊》的整个过程,看了你必会~
- C++基础 静态库与动态库
- 《重构-代码整洁之道TypeScript版》第3天
- 可读代码编写炸鸡十 - 保持单纯
- C++基础 多线程笔记(二)
- JVM详解之:HotSpot VM中的Intrinsic methods