UEFI开发学习3 -高级GUI编程
学习了简单的GUI编程后,感觉有很多的不足,因为只能简单的设置控制台的颜色,而且还只能以行和列的方式去操作,那如果想要在屏幕的任意位置画个圆,有没有办法呢?UEFI有提供了一个可以满足这个需求的Protocol:Graphics Output Protocol。
简介
Graphics Output Protocol定义在UEFI SPEC中的12.9章节,来看下它是怎么定义的:
typedef struct EFI_GRAPHICS_OUTPUT_PROTCOL { EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE QueryMode; EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE SetMode; EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT Blt; EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode; } EFI_GRAPHICS_OUTPUT_PROTOCOL;
对于用来绘图用的只需关心EFI_GRAPHICS_OUTPUT_PROTCOL.Blt()和EFI_GRAPHICS_OUTPUT_PROTCOL.mode这两个就可以了,其它的先放着。
EFI_GRAPHICS_OUTPUT_PROTCOL.Blt()
这个函数是这样的定义的:
typedef EFI_STATUS (EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT) ( IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This, IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation, IN UINTN SourceX, IN UINTN SourceY, IN UINTN DestinationX, IN UINTN DestinationY, IN UINTN Width, IN UINTN Height, IN UINTN Delta OPTIONAL );
参数说明:
This: 指的是EFI_GRAPHICS_OUTPUT_PROTCOL的这个实例
BltBuffer: 用于存放像素点RGB的颜色值
BltOperation: 调用这个函数后要进行哪种操作,有4个参数可选,分别是:
- EfiBltVideoFill - 使用BltBuffer中的值向Graphics输出,此时需要设置的参数有:输出的目标位置坐标(DestinationX, DestinationY),宽和高(Width, Height)
- EfiBltVideoToBltBuffer - 将Graphics中指定区域中的图像读到BltBuffer中,此时需要设置的参数有:源位置的坐标(SourceX, SourceY),宽和高(Width, Height)
- EfiBltBufferToVideo - 将BltBuffer中的值向Graphics输出,与第一个参数有所不同的是,此处的BltBuffer是一个数组,而第一个参数用的只是一个普通的变量,其它的使用都差不多
- EfiBltVideoToVideo - 将Graphics某区域的图像直接复制到Graphics的另一个区域,参数只需设置源位置,目标位置和宽高就可以了,其它参数会被忽略,不过当然传参数进去的时候保证能编译通过就可以了
SourceX: 源位置的X轴坐标
SourceY: 源位置的Y轴坐标
Width: 输入输出图像的宽度
Height: 输入输出图像的高度
Delta:
EFI_GRAPHICS_OUTPUT_PROTCOL.mode
这是Graphics的相关的属性值,如屏幕像素的宽和高
实例
1.取得EFI_GRAPHICS_OUTPUT_PROTCOL的实例
EFI_STATUS Status; EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; UINTN ScreenWidth, ScreenHeight; // GraphicsOutput 便是一个实例 Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput); if (EFI_ERROR (Status)) { return EFI_UNSUPPORTED; }
2.获取屏幕宽高信息
ScreenWidth = GraphicsOutput->Mode->Info->HorizontalResolution; ScreenHeight = GraphicsOutput->Mode->Info->VerticalResolution;
3.将屏幕设置为白色
思路:使用Blt()函数向屏幕画一个白色矩形
1) 设置颜色
定义一个EFI_GRAPHICS_OUTPUT_BLT_PIXEL变量存放颜色值,并且设置RGB的值为0xFFFFFF:
EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color; Color.Red = 0xFF; Color.Green = 0xFF; Color.Blue = 0xFF;
2) 调用Blt函数画一个白色矩形
Status = GraphicsOutput->Blt( GraphicsOutput, &Color, EfiBltVideoFill, 0, 0, 0, 0, // 输出位置坐标,屏幕左上角为原点 ScreenWidth, ScreenHeight, // 以原点位置根据宽高进行延申 0 );
4.画一个宽高均为100的绿色矩形,左上角位置为(200, 200)
将3中的程序改一下参数就可以了
Color.Red = 0x00; Color.Green = 0xFF; Color.Blue = 0x00; Status = GraphicsOutput->Blt( GraphicsOutput, &Color, EfiBltVideoFill, 0, 0, 200, 200, // 输出位置坐标,屏幕左上角为原点 100, 100, // 以原点位置根据宽高进行延申 0 );
5.画一条线
一条线是由很多个点组成的,也就是画一条线就是画很多个点,画点可以直接使用Blt函数,将Width和Height参数设置为1即可。但是怎么根据线的起始点跟终止点算出中间点的坐标呢?这是有算法的,称为Breshman画线,网上搜索一下就有了,我们不研究算法,直接可以拿来用即可。如下:
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; }
6.画其它图形
任意的图形最终都是由点组成的,理论上都可以画出来,只要找到算法,除了百度,Github上也有。下面是圆跟矩形的演示,算法就不贴了。
版权声明:
作者:bin
链接:https://ay123.net/mystudy/761/
来源:爱影博客
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论