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/
来源:爱影博客
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>