一、关于硬件加速
1.1 介绍
以海思平台的TDE加速显示模块为例,它主要为OSD和GUI提供快速的图形绘制功能,主要有快速位图搬移,快速色彩填充,快速抗闪搬移、快速位图缩放,画点、画水平/垂直线、位图格式转换,位图alpha叠加,位图按布尔值运算、ColorKey等操作。
1.2 应用
如何在minigui上应用这些加速模块呢?
上一章详解minigui图片加载及显示介绍了从图片解码装载到bitmap,再由bitmap刷新到fb的整个大体流程。所有图片刷新或者window刷新都离不开后面刷新到fb的流程。可以看到2.2显示过程中的最后调用的是GAL_UpdateRect其实这里就封装了硬件快速位图搬移的操作接口。
minigui上提供了newgal(graphic abstract layer)硬件抽象层。也就是该层提供了所有硬件操作的方法接口。那么只要实现GAL_VideoDevice这个结构体句柄就可以在调用GAL_UpdateRect时调用到device->UpdateRects = MStar_DirectUpdate;具体硬件的位图搬移。
二、主要的接口实现
下面为minigui较为常用的硬件加速接口
struct GAL_VideoDevice {
int (*VideoInit)(_THIS, GAL_PixelFormat *vformat); //平台相关FB的硬件初始化函数
GAL_Surface *(*SetVideoMode)(_THIS, GAL_Surface *current,
int width, int height, int bpp, Uint32 flags); //这个可以认为是系统surface可以直接与FB相绑定也可以自己虚拟一个给他,这个是导入到系统使用的也就是bitblt最终会搬移到这个surface上
void (*UpdateRects)(_THIS, int numrects, GAL_Rect *rects); //快速位图搬移,应用于区域图形更新可以是一个window也可以是一个图标
int (*AllocHWSurface)(_THIS, GAL_Surface *surface); //为硬件操作分配dma或者其他特定连续内存
int (*CheckHWBlit)(_THIS, GAL_Surface *src, GAL_Surface *dst); // 查询硬件是否提供了快速位图搬移操作接口,即是否实现了下面的GAL_blit硬件操作接口函数将其返回给surface src的src->map->hw_blit = MStar_HWAccelBlit;
int (*FillHWRect)(_THIS, GAL_Surface *dst, GAL_Rect *rect, Uint32 color);//快速色彩填充
int (*SetHWColorKey)(_THIS, GAL_Surface *surface, Uint32 key); //ColorKey操作
int (*SetHWAlpha)(_THIS, GAL_Surface *surface, Uint8 value); //位图alpha叠加
void (*FreeHWSurface)(_THIS, GAL_Surface *surface); //释放AllocHWSurface分配的存储空间
}
//以及GAL_Surface GAL_BlitMap的私有快速位图搬运接口
typedef int (*GAL_blit)(struct GAL_Surface *src, GAL_Rect *srcrect,
struct GAL_Surface *dst, GAL_Rect *dstrect);
//该接口是private DC或者window second DC所私有的surface的快速位图搬运接口。后面会说明不同dc的位图刷新过程。
不同的硬件平台有各自不同的TDE加速模块。所以具体的实现就不说明了。需要读者根据不同平台去做适配,但是适配之前你需要明白这些接口是做什么用的,还有什么时候才会调用到这些接口。
三、何时会调用到这些加速接口
上一篇文章详解minigui图片加载及显示只提到了什么时候会调用到device->UpdateRects,也就是可以调用FillBoxWithBitmap就可以使用到快速图形搬移了。试想如果你整个UI都使用FillBoxWithBitmap 来画那将会在视觉上看到图标是一个个贴出来的。这样就显得不流畅自然。所以我们可以借助minigui给我们的window DC来先画好一整个window子窗口再将其通过 UpdateRects 的方式贴合到FB上。具体的流程如下:
3.1 应用层:
创建一个dialogbox
static DLGTEMPLATE DlgInitProgress =
{
WS_BORDER | WS_CAPTION,
WS_EX_AUTOSECONDARYDC,
120, 150, 400, 140,
the_system_is_initializing,
0, 0,
3, NULL,
0
};
static CTRLDATA CtrlInitProgress [] =
{
{
"button",
WS_TABSTOP | WS_VISIBLE | BS_DEFPUSHBUTTON,
170, 70, 60, 25,
IDOK,
OK,
0
}
};
static int InitDialogBoxProc (HWND hDlg, int message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case MSG_INITDIALOG:
return 1;
case MSG_COMMAND:
switch (wParam) {
case IDOK:
case IDCANCEL:
EndDialog (hDlg, wParam);
break;
}
break;
case MSG_PAINT:
//画多个图标到secondary DC
hdc = BeginPaint (hWnd);
FillBoxWithBitmap (hdc, 0, 0, 100, 100, &bmp);
Rectangle (hdc, 0, 0, 100, 100);
FillBoxWithBitmap (hdc, 100, 0, 200, 200, &bmp);
Rectangle (hdc, 100, 0, 300, 200);
FillBoxWithBitmapPart (hdc, 0, 200, 150, 150, 200, 200, &bmp, 10, 10);
Rectangle (hdc, 0, 200, 400, 400);
EndPaint (hWnd, hdc);
break;
case MSG_CLOSE:
EndDialog (hDlg, IDCANCEL);
break;
}
return DefaultDialogProc (hDlg, message, wParam, lParam);
}
static void InitDialogBox (HWND hWnd)
{
DlgInitProgress.controls = CtrlInitProgress;
DialogBoxIndirectParam (&DlgInitProgress, hWnd, InitDialogBoxProc, 0L);
}
int MiniGUIMain (int argc, const char* argv[])
{
#ifdef _MGRM_PROCESSES
JoinLayer(NAME_DEF_LAYER , "dialogbox" , 0 , 0);
#endif
InitDialogBox (HWND_DESKTOP);
return 0;
}
#ifdef _MGRM_THREADS
#include <minigui/dti.c>
#endif
你可以在这个dialogbox上用 FillBoxWithBitmap 来画你的背景图或者图标,但是前提是这个dialog的DLGTEMPLATE 模板 的属性必须得是WS_EX_AUTOSECONDARYDC /*The window creates its own secondary device context automatically.*/
这样dialog就会自动创建一个子DC,子DC的surface就是该窗口画图的层画完之后调用EndPaint 就会再调用到UpdateRects去更新该窗口到FB,这时候就是整块刷了,不会看到一个个小图标渐次出来。
具体的流程如下图所示
middle layer就是minigui所谓的双buffer,这样在画图的时候就是转到后台操作了,故在画图的时候显示不会出现闪烁的情况。可以看到箭头处三个关键函数GAL_PutBox GAL_blit UpdateRects 作用分别是:
GAL_PutBox 是软件算法函数用于将FillBoxWithBitmap填充进来的icon bitmap画到预先malloc的dc mem上,当画好整个页面后EndPaint会将window rect通过bitblt GAL_Blit画到中间图层,接着在bitblt的最后调用UpdateRects画到最终的图层,这个流程可以简化到将updateRects的操作搬移到GAL_blit上从而减少拷贝次数,后面的updateRects有点没必要了(前提是你用的是单buffer)。详细流程看上图。
如果你使用的realbuffer是双buffer那么还是用使用上次流程图。在updateRect的时候先更新到后台buffer再切换buffer显示,再更新到之前使用的buffer,保存双buffer上的内容的一致性。