继续看了下sdlpal的源码,这节讲下代码是如何从资源文件中读取所需图片并显示到屏幕上的。
下面这张就是开始界面了,当然除了背景图外还有菜单,菜单里的两个选项其实就是绘制的两行文字(一般的菜单除了文字还会有菜单背景图的,这里没有,只是简单的文字), 菜单的原理就另外章节讲了,这里只讲下图片资源的加载和显示。
一路跟踪,发现背景是在这里绘制的:mian()->PAL_GameMain()->PAL_OpeningMenu()->PAL_DrawOpeningMenuBackground()
,当然还有很多其他代码,这里不用理,弄明白最后那个函数就可以了。这些函数具体在哪个文件就不说了,用VS Code查找很方便。看下该函数的内部实现:
/* From: sdlpal/uigame.c */
// 可以看到函数并没有多少行代码
VOID
PAL_DrawOpeningMenuBackground(
VOID
)
/*++
Purpose:
Draw the background of the main menu.
Parameters:
None.
Return value:
None.
--*/
{
// LPBYTE的原型是unsigned char*,该变量用来存放从那些mkf读取到的图片资源数据
LPBYTE buf;
// 之所以要malloc这么多个字节,是因为开始界面那张背景图的分辨率是320 * 200的
// 因此我们要用320 * 200个字节来保存图片数据
buf = (LPBYTE)malloc(320 * 200);
if (buf == NULL)
{
return;
}
//
// Read the picture from fbp.mkf.
//
// 从上面源码的注释可知道,背景图是压缩在fbp.mkf这个资源文件中的
// 函数第一个参数是用来写入数据的,第二个参数指定了写入数据的大小,第三个参数应该是用来说明该图片在资源文件中的位置信息,最后一个参数当然是fbp.mkf这个资源文件了
// 该函数的内部实现先不看,等看明白了mfk资源文件的结构后,在开一篇单独的文章记录一下
// 现在只要知道-调用这个函数后,我们就从资源文件中读取到了背景图的数据信息
PAL_MKFDecompressChunk(buf, 320 * 200, MAINMENU_BACKGROUND_FBPNUM, gpGlobals->f.fpFBP);
//
// ...and blit it to the screen buffer.
//
// 读取到图片后,当然需要将它绘制到屏幕上,下面这个函数就是专门负责将图片数据填充到屏幕上的
// gpScreen在VIDEO_Startup()中创建好了,下面这个函数只是将buf数据填充到gpScreen->pixels中
// 该函数的内部实现,下面会讲到
PAL_FBPBlitToSurface(buf, gpScreen);
// 数据填充好后,还需刷新下屏幕才可以显示到屏幕上的,该函数下面会讲
VIDEO_UpdateScreen(NULL);
// 其实调用PAL_FBPBlitToSurface(buf, gpScreen)后,已经将buf这段内存中的数据转换成了SDL_Surface*这种类型的数据(也就是gpScreen),因此可以将buf释放掉了
free(buf);
}
如果你使用过SDL2在一个窗口显示一张图片,那么你肯定知道它的大致过程是这样的:
// 加载一个bmp文件
SDL_Surface* surface = SDL_LoadBMP("foo.bmp");
// 将surface转换成texture
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
// 拷贝到渲染器上
SDL_RenderCopy(renderer, texture, NULL, NULL);
// 刷新显示
SDL_RenderPresent(renderer);
sdlpal开始界面的背景图显示原理也和这个差不多,不过背景图并不是从一个bmp文件中加载的,而是从mkf文件中读取数据到内存,然后将内存中的数据转换成一个SDL_Surface*
。
// lpBitmapFBP是从fbp.mkf文件中读取到的bmp图片文件的数据,它其实就是一个unsigned char类型的数组
// 数组的每个元素记录着图片的像素信息。lpDstSurface就是转换后的数据了。
// 从下面的NOTE注释可以看出,该函数有个限制,它要求图片的分辨率是320*200像素的,如果读取到一个不符
// 合该分辨率的图片数据到lpBitmapFBP,再调用该函数是不会创建出plDstSurface的。
INT
PAL_FBPBlitToSurface(
LPBYTE lpBitmapFBP,
SDL_Surface *lpDstSurface
)
/*++
Purpose:
Blit an uncompressed bitmap in FBP.MKF to an SDL surface.
NOTE: Assume the surface is already locked, and the surface is a 8-bit 320x200 one.
Parameters:
[IN] lpBitmapFBP - pointer to the RLE-compressed bitmap to be decoded.
[OUT] lpDstSurface - pointer to the destination SDL surface.
Return value:
0 = success, -