位图和Blitting
你以前可能听说过位图; 可能是过时的“.bmp”图像。好吧,位图在技术上只是一种图像存储的格式,其中像素数据由一块内存表示,或者你可以说是一个位图。“bmp”格式表示在标题之后顺序存储每个像素(单色,RGB,RGBA等)的颜色值的图像。其他格式,如“png”和“jpg”仍然是位图,但利用压缩技术来减少文件大小(理想情况下),而不会牺牲图像质量。
在软件(CPU)2D渲染中,一旦将位图加载到存储器中,CPU就简单地将像素值从加载的位图修改和/或复制到表示窗口的区域。这被称为“blitting”。Blitting可能比简单地复制像素更复杂; 它也可能涉及调整大小,拉伸,翻转或以其他方式后处理图像。
SDL提供加载和blitting位图的功能。blitting API可能很有用,但它相对有限。默认情况下,SDL只能加载“bmp”格式位图 - 扩展名SDL_Image必须用于加载更多图像格式,例如“png”,“jpg”,“gif”或“tif”。我们将在第07课中了解更多扩展库。此外,在第05课中,我们将了解功能更全面的硬件(GPU)渲染API。
加载位图
上一课,我提到了SDL如何将像素内存提取到曲面。曲面可以表示加载的位图,您正在创建的新纹理,甚至是您要绘制的窗口。
要将位图加载到曲面中,请使用函数SDL_LoadBMP()。它的用法非常简单 - 只需向您发送要加载的.bmp文件的名称(相对于可执行文件的位置)。如果加载成功,您将收到一个指向包含位图数据的新SDL_Surface的指针。如果加载失败,该函数将返回NULL。
SDL_Surface* image = SDL_LoadBMP( "image.bmp" );
if ( !image ) {
// load failed
}
释放曲面就像分配曲面一样简单 - 当曲面不再使用时,函数SDL_FreeSurface()可用于销毁它并释放内存。
SDL_FreeSurface( image );
保存位图
当然,加载位图的反义词是保存一个。函数SDL_SaveBMP()就是这样做的。将包含位图数据的表面和要保存的位图文件的名称传递给它。该函数将在成功时返回0或在失败时返回负值。
int result = SDL_SaveBMP( surface, "saved.bmp" );
if ( result < 0 ) {
// save failed
}
Blitting to the Window
blit到表面的基本方法是使用SDL_BlitSurface()。该函数采用源曲面和区域,并将像素blit到目标曲面和区域。这些区域使用SDL_Rect结构指定。
源矩形包含要复制的源曲面的区域的坐标和大小。可以传递NULL而不是此矩形以使用整个表面。实际只使用目标矩形的x和y坐标,剪切将自动完成。
SDL_Rect dest;
dest.x = 100;
dest.y = 50;
int result = SDL_BlitSurface( surface, NULL, windowSurface, &dest );
if ( result < 0 ) {
// blit failed
}
优化的表面
虽然不是绝对必要,但是可以相对于另一个表面优化表面以提高blits的速度。从本质上讲,这只是意味着更改曲面的像素格式以匹配另一个。然后,程序可以在blitting时简单地复制数据,而不是必须更改格式。
这是通过函数SDL_ConvertSurface()完成的。此函数采用您要转换的曲面和要转换的格式。此格式在目标曲面的“format”数据成员中指定,该成员几乎应始终为窗口曲面。如果转换成功,则该函数返回一个新的转换曲面。它不会改变或释放原始(未转换的)表面 - 你必须自己做。
SDL_Surface* load = SDL_LoadBMP("test.bmp");
SDL_Surface* image = SDL_ConvertSurface( load, windowSurface->format, 0 );
if ( !image ) {
// convert failed
} else {
// convert succeeded
SDL_FreeSurface( load );
load = NULL;
}
缩放
使用SDL_BlitSurface(),您可以在任何位置标记位图。但是,我提到只考虑目标矩形的x和y坐标。这意味着每次blitted图像的大小相同。要拉伸或缩放位图以适合任意区域,可以使用函数SDL_BlitScaled()。此函数采用与SDL_BlitSurface()相同的参数,不同之处在于它考虑了目标矩形的宽度和高度。
SDL_Rect dest;
dest.x = 100;
dest.y = 50;
dest.w = 200;
dest.h = 100;
int result = SDL_BlitScaled( surface, NULL, windowSurface, &dest );
if ( result < 0 ) {
// blit failed
}
实例代码如下:
#include <iostream>
#include <SDL2/SDL.h>
using namespace std;
bool init();
void kill();
bool load();
// Variables to hold our window and surfaces
SDL_Window* window;
SDL_Surface* winSurface;
SDL_Surface* image1;
SDL_Surface* image2;
int main(int argc, char** args) {
if ( !init() ) return 1;
if ( !load() ) return 1;
// Blit image to entire window
SDL_BlitSurface( image1, NULL, winSurface, NULL );
// Blit image to scaled portion of window
SDL_Rect dest;
dest.x = 160;
dest.y = 120;
dest.w = 320;
dest.h = 240;
SDL_BlitScaled( image2, NULL, winSurface, &dest );
// Update window
SDL_UpdateWindowSurface( window );
SDL_Delay(5000);
kill();
return 0;
}
bool load() {
// Temporary surfaces to load images into
// This should use only 1 temp surface, but for conciseness we use two
SDL_Surface *temp1, *temp2;
// Load images
temp1 = SDL_LoadBMP("test1.bmp");
temp2 = SDL_LoadBMP("test2.bmp");
// Make sure loads succeeded
if ( !temp1 || !temp2 ) {
cout << "Error loading image: " << SDL_GetError() << endl;
return false;
}
// Format surfaces
image1 = SDL_ConvertSurface( temp1, winSurface->format, 0 );
image2 = SDL_ConvertSurface( temp2, winSurface->format, 0 );
// Free temporary surfaces
SDL_FreeSurface( temp1 );
SDL_FreeSurface( temp2 );
// Make sure format succeeded
if ( !image1 || !image2 ) {
cout << "Error converting surface: " << SDL_GetError() << endl;
return false;
}
return true;
}
bool init() {
// See last example for comments
if ( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ) {
cout << "Error initializing SDL: " << SDL_GetError() << endl;
return false;
}
window = SDL_CreateWindow( "Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_SHOWN );
if ( !window ) {
cout << "Error creating window: " << SDL_GetError() << endl;
return false;
}
winSurface = SDL_GetWindowSurface( window );
if ( !winSurface ) {
cout << "Error getting surface: " << SDL_GetError() << endl;
return false;
}
return true;
}
void kill() {
// Free images
SDL_FreeSurface( image1 );
SDL_FreeSurface( image2 );
// Quit
SDL_DestroyWindow( window );
SDL_Quit();
}
Makefile如下:
运行结果如下:
例程下载地址:
https://download.****.net/download/cyf15238622067/10697304