Step to UEFI (235)UEFI Shell 下的变幻线

CRT(阴极射线显像管)显示器的显像原理主要是由灯丝加热阴极,阴极发射电子,然后在加速极电场的作用下,经聚焦极聚成很细的电子束,在阳极高压作用下,获得巨大的能量,以极高的速度去轰击荧光粉层。这些电子束轰击的目标就是荧光屏上的三原色。为此,电子枪发射的电子束不是一束,而是三束,它们分别受电脑显卡R、 G、 B三个基色视频信号电压的控制,去轰击各自的荧光粉单元,从而在显示屏上显示出完整的图像。

在图形界面的操作系统下,显示屏上显示的色彩多种多样,当用户停止对电脑进行操作时,屏幕显示就会始终固定在同一个画面上,即电子束长期轰击荧光层的相同区域,长时间下去,会因为显示屏荧光层的疲劳效应导致屏幕老化,甚至是显像管被击穿。因此从Windows 3.X时代至今,屏幕保护程序一直作为保护CRT显示屏的最佳帮手,通过不断变化的图形显示使荧光层上的固定点不会被长时间轰击,从而避免了屏幕的损坏。【参考2】

比如,下面这个照片就是游戏《吃豆人》的烧屏,《吃豆人》历史久远,画面对比度高且单一,又是热门游戏,所以很多老机台烧屏都十分严重。【参考3】

吃豆人烧屏

随着时代的发展,屏保除了展示信息已经没有太多意义了。Windows XP 开始,内置了一个非常经典的“变换线”屏保。这次的实验就是在 UEFI Shell 下实现这个功能。

关键部分参考了《模拟经典屏保“变幻线”》【参考1】,主要是绘制直线(采用打点组成直线的方法,效率不高)。

代码如下:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>
#include  <stdio.h>
#include  <stdlib.h>
#include  <math.h>

#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>
#include <Protocol/GraphicsOutput.h>


extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE          *gST;

EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
EFI_GRAPHICS_OUTPUT_PROTOCOL          *GraphicsOutput = NULL;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL         PixelColor= {230,17,219,0};
        
#define HIGH 600
#define WIDTH 800
#define RADIUS 20
#define PN 5

typedef struct Point
{
        int x;
        int y;
        int velocity_x;
        int velocity_y;
} VPOINT;

static VPOINT vpoint[PN];

int
abs(int j)
{
        return(j < 0 ? -j : j);
}

EFI_STATUS DrawPoint(
        IN EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput,
        IN UINTN x,
        IN UINTN y,
        IN UINTN Width,
        IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *PixelColor
)
{
        EFI_STATUS Status;

        Status = GraphicsOutput->Blt(
                         GraphicsOutput,
                         PixelColor,
                         EfiBltVideoFill,
                         0, 0,
                         x, y,
                         Width, Width,
                         0
                 );

        return EFI_SUCCESS;
}

EFI_STATUS DrawLine(
        IN EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput,
        IN UINTN x0, UINTN y0, UINTN x1, UINTN y1,
        IN UINTN BorderWidth,
        IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BorderColor
)
{
        INT32 dx  = abs((int)(x1 - x0));
        INT32 sx  = x0 < x1 ? 1 : -1;
        INT32 dy  = abs((int)(y1-y0)), sy = y0 < y1 ? 1 : -1;
        INT32 err = ( dx > dy ? dx : -dy) / 2, e2;

        for(;;)
        {
                DrawPoint(GraphicsOutput, x0, y0, BorderWidth, BorderColor);

                if (x0==x1 && y0==y1) break;

                e2 = err;

                if (e2 > -dx)
                {
                        err -= dy;
                        x0 += sx;
                }
                if (e2 <  dy)
                {
                        err += dx;
                        y0 += sy;
                }
        }
        return EFI_SUCCESS;
}

void show()
{
        gST -> ConOut -> ClearScreen(gST->ConOut);

        for (int i = 0; i < PN; i++)
        {
                if ((vpoint[i].x <= 0) || (vpoint[i].x >= WIDTH))
                        vpoint[i].velocity_x = -vpoint[i].velocity_x;
                if ((vpoint[i].y  <= 0) || (vpoint[i].y  >= HIGH))
                        vpoint[i].velocity_y = -vpoint[i].velocity_y;

                vpoint[i].x += vpoint[i].velocity_x;
                vpoint[i].y += vpoint[i].velocity_y;

                for (int j = 0; j < PN - 1; j++)
                {
                        DrawLine(
                                GraphicsOutput,
                                vpoint[j].x,vpoint[j].y,
                                vpoint[j + 1].x,vpoint[j + 1].y,
                                1,
                                &PixelColor);
                }
                DrawLine(
                        GraphicsOutput,
                        vpoint[0].x, vpoint[0].y,
                        vpoint[PN - 1].x, vpoint[PN - 1].y,
                        1,
                        &PixelColor);
        }
}

int
EFIAPI
main (
        IN int Argc,
        IN char **Argv
)
{
        EFI_STATUS      Status;
        EFI_INPUT_KEY   Key;
        int             a;
        
        Status = gBS->LocateProtocol(
                                &GraphicsOutputProtocolGuid, 
                                NULL, 
                                (VOID **) &GraphicsOutput);
        if (EFI_ERROR(Status))
        {
                GraphicsOutput = NULL;
                Print(L"Loading Graphics_Output_Protocol error!\n");
                return EFI_SUCCESS;
        }

        for (int i = 0; i < PN; i++)
        {
                vpoint[i].x = rand() % (WIDTH - RADIUS) + RADIUS;
                vpoint[i].y = rand() % (HIGH - RADIUS) + RADIUS;
                a = rand() % 361;
                vpoint[i].velocity_x = (int)((RADIUS / 5) * cos(a));
                vpoint[i].velocity_y = (int)((RADIUS / 5) * sin(a));
        }

        for (int i = 0; i < PN - 1; i++)
        {
                DrawLine(
                        GraphicsOutput,
                        vpoint[i].x,vpoint[i].y,
                        vpoint[i + 1].x,vpoint[i + 1].y,
                        1,
                        &PixelColor);
        }
        DrawLine(
                GraphicsOutput,
                vpoint[0].x, vpoint[0].y,
                vpoint[PN - 1].x, vpoint[PN - 1].y,
                1,
                &PixelColor);


        Key.ScanCode=SCAN_NULL;
        while (SCAN_DOWN!=Key.ScanCode)
        {
                show();
                Status= gST -> ConIn -> ReadKeyStroke(gST->ConIn,&Key);
        }
        
        return EFI_SUCCESS;

}

运行之后按向下的方向键退出代码。运行结果:

UEFI Shell 下面的变换线

源代码和编译后的 X64程序可以在这里下载:

参考:

  1. https://blog.csdn.net/weixin_48758682/article/details/108680035
  2. https://baike.baidu.com/item/%E5%B1%8F%E5%B9%95%E4%BF%9D%E6%8A%A4/6124893?fr=aladdin#2
  3. https://tieba.baidu.com/p/5041549602?red_tag=3176677362

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注