请保留-> 【原文: https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
syslibforuefi中,还有几个小程序。从我的角度来看,最值得学习的是他将图像变为HII资源的方式,其他图形图像的知识,在很久以前的博客中,已经研究了很多了。
早上打开CSDN,让我吃了一惊:
悄没声息地跑到了游戏领域内容榜的第2名了!
上次我就说过,我这是UEFI开发的博客啊,又不是介绍游戏开发的。这奇怪的评价,让我都不知道从哪里吐槽了。
我是不是转行去开发游戏比较好?
算了,今天把syslibforuefi中的温度计小游戏移植下,看看效果。这是syslibforuefi的最后一篇了,代码也读得差不多了。
1 程序设计
温度计程序是syslibforuefi的ImageStack工程,其主要功能如下:
1) 显示温度计图像;
2) 当用户按上下键时,温度计的水银部分随之升高或降低。
也就是说,需要动态处理的部分,只是水银部分,其他当做背景图案处理就可以了。
用来实现图像拉伸和收缩的函数为StrethcImage(),而显示函数为DisplayImageStack()。DisplayImageStack()与前两篇的同名函数不同,其实现经过了改造,具体实现可以在我新建的工程中查看(见文末的地址)。
2 移植和编写代码
原来的代码中,仍旧使用了HII资源的方式,这部分需要修改。为了方便移植,所有代码都在一个源文件中了,没有根据功能进行拆分。基本的移植步骤如下:
1)重新实现图像的加载方法
需要加载的图像有三个:bg.bmp、front.bmp和mid.bmp。加载的目标是将图像拷贝到EFI_GRAPHICS_OUTPUT_BLT_PIXEL型指针所指向的内存中。因此,可以使用上一篇的LoadFile()和LoadBitmapFile()。
将原来工程中的SetUpImages()改为如下内容:
EFI_STATUS
SetUpImages (
IN CONST CHAR8 *BgLogoFilePath,
IN CONST CHAR8 *MidLogoFilePath,
IN CONST CHAR8 *FrontLogoFilePath)
{
// Initialize the graphics support.
if (!InitGop()) {
return EFI_UNSUPPORTED;
}
// Load the background bitmap.
if (!LoadBitmapFile(BgLogoFilePath, &mBgBlt, &mBgHeight, &mBgWidth)) {
return EFI_UNSUPPORTED;
}
if (!LoadBitmapFile(MidLogoFilePath, &mMidBlt, &mMidHeight, &mMidWidth)) {
return EFI_UNSUPPORTED;
}
MaxHeight = mMidHeight;
if (!LoadBitmapFile(FrontLogoFilePath, &mFrontBlt, &mFrontHeight, &mFrontWidth)) {
return EFI_UNSUPPORTED;
}
return EFI_SUCCESS;
}
2)图像显示函数
由于图像会变动,因此增加了一个处理函数CopyBitmapToBitmap(),用来处理水银部分的变动。
EFI_STATUS CopyBitmapToBitmap (
IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Dest,
IN UINTN DestWidth,
IN UINTN DestHeight,
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Src,
IN UINTN SrcWidth,
IN UINTN SrcHeight,
IN UINTN OffsetX,
IN UINTN OffsetY
)
{
UINTN CurrentX;
UINTN CurrentY;
UINTN SourceX;
UINTN SourceY;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL Current;
Current.Red = 0;
Current.Green = 0;
Current.Blue = 0;
SourceX = 0;
SourceY = 0;
CurrentX = OffsetX;
CurrentY = OffsetY;
if (CurrentX > DestWidth || CurrentY > DestHeight) {
return EFI_INVALID_PARAMETER;
}
//copy image
//if source would go over edge of destination buffer, then clip edges
while (SourceY < SrcHeight && CurrentY < DestHeight) {
while (SourceX < SrcWidth && CurrentX < DestWidth) {
Current = Src[(SourceY * SrcWidth) + SourceX];
if (! (Current.Red == 0 && Current.Green == 0 && Current.Blue == 0)) {
Dest[(CurrentY * DestWidth) + CurrentX] = Src[(SourceY * SrcWidth) + SourceX];
}
SourceX++;
CurrentX++;
}
SourceX = 0;
CurrentX = OffsetX;
SourceY++;
CurrentY++;
}
return EFI_SUCCESS;
}
而图像显示函数DisplayImageStack()则调用CopyBitmapToBitmap(),进行实际的显示处理。
EFI_STATUS DisplayImageStack ()
{
UINTN CoordinateX;
UINTN CoordinateY;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt;
Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) malloc(mBgWidth * mBgHeight * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
memset (Blt, 0, mBgWidth * mBgHeight * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
CopyBitmapToBitmap (Blt, mBgWidth, mBgHeight, mBgBlt, mBgWidth, mBgHeight, 0, 0);
CoordinateX = (mBgWidth - mMidWidth) / 2;
CoordinateY = 15 + (MaxHeight - mMidHeight);
CopyBitmapToBitmap (Blt, mBgWidth, mBgHeight, mMidBlt, mMidWidth, mMidHeight, CoordinateX, CoordinateY);
CoordinateX = (mBgWidth - mFrontWidth) / 2;
CoordinateY = 15;
CopyBitmapToBitmap (Blt, mBgWidth, mBgHeight, mFrontBlt, mFrontWidth, mFrontHeight, CoordinateX, CoordinateY);
CoordinateX = (mGraphicsOutput->Mode->Info->HorizontalResolution / 2) - (mBgWidth / 2);
CoordinateY = (mGraphicsOutput->Mode->Info->VerticalResolution / 2) - (mBgHeight / 2);
mGraphicsOutput->Blt (
mGraphicsOutput,
Blt,
EfiBltBufferToVideo,
0,
0,
CoordinateX,
CoordinateY,
mBgWidth,
mBgHeight,
mBgWidth * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
);
return EFI_SUCCESS;
}
3)用户操作处理
主函数main()中调用StretchImage(),处理用户输入的上下方向键,控制水银的上升和下降。实现如下:
EFI_STATUS StretchImage ()
{
EFI_INPUT_KEY Key;
UINTN index;
//clear screen
gST->ConOut->Reset (gST->ConOut, FALSE);
DisplayImageStack();
//input loop
do {
gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &index);
gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
if (Key.ScanCode != SCAN_NULL) {
if (Key.ScanCode == SCAN_UP) {
// Print(L"UP");
RotateUp ();
} else if (Key.ScanCode == SCAN_DOWN) {
// Print(L"Down");
RotateDown();
}
}
DisplayImageStack();
} while (Key.ScanCode != SCAN_END);
//clear screen after app exits
gST->ConOut->Reset (gST->ConOut, FALSE);
return EFI_SUCCESS;
}
水银下降上升的处理函数为RotateDown()和RotateUp(),它对水银部分的长度进行更改,通过DisplayImageStack()显示处理。
3 测试温度计小游戏
使用如下命令编译:
C:\vUDK2018\edk2>build -p RobinPkg\RobinPkg.dsc -m RobinPkg\Applications\ImageStack\ImageStack.inf -a IA32
把编译好的程序ImageStack.efi以及项目文件夹下的:bg.bmp、front.bmp和mid.bmp拷贝到模拟器所在文件夹,运行ImageStack.efi即可看到效果。
在Tianocore模拟器中,运行效果如图2所示。
实际写完后,感觉功能有点简单,和之前移植过的GuiLite的某个程序很像。如果用博客中之前写好的图形库,也能很容易的构建出来。
后面对syslibforuefi的代码就不再探索了,有兴趣的网友可以自己下载读读看。
Gitee地址:https://gitee.com/luobing4365/uefi-explorer
项目代码位于:/ FF RobinPkg/RobinPkg/Applications/ImageStack下
2,047 total views, 1 views today