UEFI开发探索96 – 温度计小游戏

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】

syslibforuefi中,还有几个小程序。从我的角度来看,最值得学习的是他将图像变为HII资源的方式,其他图形图像的知识,在很久以前的博客中,已经研究了很多了。

早上打开CSDN,让我吃了一惊:

图1 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所示。

图2 温度计小游戏

实际写完后,感觉功能有点简单,和之前移植过的GuiLite的某个程序很像。如果用博客中之前写好的图形库,也能很容易的构建出来。

后面对syslibforuefi的代码就不再探索了,有兴趣的网友可以自己下载读读看。

Gitee地址:https://gitee.com/luobing4365/uefi-explorer
项目代码位于:/ FF RobinPkg/RobinPkg/Applications/ImageStack下

1,596 total views, 4 views today

发表评论

电子邮件地址不会被公开。