博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用C++做TerraExplorer开发(四)——实现HUD Layer(1)(转载)
阅读量:7088 次
发布时间:2019-06-28

本文共 3444 字,大约阅读时间需要 11 分钟。

转自:http://www.hailongchang.org/index.php/archives/201

用C++做TerraExplorer开发(四)——实现HUD Layer(1)

前面的链接:

版权所有, 转载请注明:

使用TerraExplorer开发项目,有一个重量级的标杆横亘在我们这些开发者的面前,那就是,很难不让用户拿我们的软件系统与Google Earth做比较,而结果我想大多数情况下都是我们自惭形秽了。从本期开始,我将介绍一个重要话题,使得我们起码在界面上不输Google Earth,甚至超越Google Earth。这就是HUD Layer。

对于HUD Layer的具体概念,大家可以看一下,其实TE里右上角的那个罗盘就是一个典型的HUD。

用过Google Earth的人都知道,它的界面在窗口底部会有一段字符串用来显示鼠标移动的坐标,靠右一点还有一个Google的logo。实际上,实现类似HUD Layer的方法有很多种, 我会在接下来的文章中,介绍三种用来实现HUD Layer的方法。第一次,我们要中规中矩一些,先来看看Manual里面是怎么说的。

也许有人注意到过,在手册_ITerraExplorerEvents下有一个函数:

HRESULT OnDrawHUD()

看看里面的介绍,这个函数每一帧都会被触发,具体时间是在当前帧渲染完成后和HUD Layer被绘制之前。在这个函数内,我们可以使用IRender5::SetHUDLayer()来进行HUD Layer的绘制和更新。 上面的信息讲得很明白,那就是要绘制HUD Layer,我们必须响应OnDrawHUD动作,并且在该动作内要用SetHUDLayer这个函数进行必要的绘制工作。大致原理就是如此,看来关键还在于如何绘制HUD Layer。

SetHUDLayer只有三个参数,第一个是IStream指针,剩下的两个保留,我们只需要简单传个0就可以了。而对于IStream,手册上说” Meta DC saved as a stream. Passing Null clears the current HUD layer.” 也就是说这个Stream保存的是一个Meta DC, 也就是GDI中的元文件(Meta File),如果传递一个NULL,那么所有的HUD Layer都会被清除掉。这里要提醒大家的是,我们所有的绘制动作必须保存在MetaFile中才能显示为正确的HUD Layer。 我在刚接触HUD Layer时就曾经试图将所有动作绘制到位图中,可惜最终无法正确显示。

Meta File是以二进制形式存储的GDI命令的集合。也就是我们所有的绘制动作都以指令的形式保存在元文件中。创建元文件我们使用的是GDI函数CreateEnhMetaFile。需要为该函数传递一个源设备描述表的句柄(HDC)和一个指向矩形范围RECT的指针,具体用法请参阅MSDN。

创建了MetaFile之后,我们会得到该元文件的句柄,然后就可以使用改句柄来做一些绘制工作了,别如BitBlt,TextOut等等GDI提供的绘制函数。但是要注意的是,MetaFile是独立于硬件的,因此创建时传递的RECT指针,我们要在RECT内部使用毫米,而绝大部分GDI函数使用的是像素,因此还需要事先更改一下GDI的映射模式。当然如果习惯于使用像素,比如像我,那大可以不用更改,但必须保证有两个同等大小的RECT,一个用于创建MetaFile,另外一个用于做常规的GDI操作。

有了元文件之后,我们可以使用OleCreatePictureIndirect创建一个IPicutre对象,然后在内存中分配Stream,接着使用IPersistStream的Save方法保存为一个IStream对象。有了IStream的对象,最后一步就是调用SetHUDLayer完成绘制了。

上面的整个过程也许有点复杂,但其实基本步骤就两个:

1: 创建MetaFile保存绘制

2: 将MetaFile转换为IStream。

下面看看核心的代码,我需要在窗口底部居中的位置显示一个字符串”Hello World”,并且在底部靠右显示一幅含有logo的PNG图片,我们知道PNG图片带Alpha通道,因此还可以显示出类似Google logo那样镂空的图片来。

HRESULT KTE
::OnDrawHud()
{
chlASSERT(m_hwnd !0);
HRESULT hr;
HDC hdc = GetWindowDC(m_hwnd);
RECT rcClient;
GetClientRect(m_hwnd,&rcClient);
int iWidthMM = GetDeviceCaps(hdc, HORZSIZE);
int iHeightMM = GetDeviceCaps(hdc, VERTSIZE);
int iWidthPels = GetDeviceCaps(hdc, HORZRES);
int iHeightPels = GetDeviceCaps(hdc, VERTRES);
//设置meta file的边界框,注意其单位是毫米,所以要做一下转换
RECT rc = rcClient;
rc.left (rc.left * iWidthMM 100)/iWidthPels;
rc.top (rc.top * iHeightMM 100)/iHeightPels;
rc.right (rc.right * iWidthMM 100)/iWidthPels;
rc.bottom (rc.bottom * iHeightMM 100)/iHeightPels;
HDC metaDC = CreateEnhMetaFile(hdc,NULL,&rc,0);
chlASSERT(metaDC != INVALID_HANDLE_VALUE);
std::wstring shint = _T("Hllo world");
//设置文字输出背景并居中显示
SetBkMode(metaDC,TRANSPARENT);
SetTextColor(metaDC,RGB(0xff,0xff,0xff));
SIZE sz {
0};
GetTextExtentPoint32(metaDC,shint.c_str(),shint.length(),&sz);
TextOut(metaDC,rcClient.left (rcClient.right - rcClient.left - sz.cx)/2,rcClient.bottom - sz.cy,
shint.c_str(),shint.length());
//设置右下角显示图片
CImage img;
std::wstring pngname = absPath(+ _T("\\logo.png");
hr = img.Load(pngname.c_str());
chlASSERT(SUCCEEDED(hr));
img.BitBlt(metaDC,rcClient.right - img.GetWidth(),rcClient.bottom - img.GetHeight(),SRCCOPY);
HENHMETAFILE hmetaf=CloseEnhMetaFile(metaDC);
PICTDESC picdesc {
sizeof(PICTDESC), PICTYPE_ENHMETAFILE};
picdesc.emf.hemf = hmetaf;
IPicturePtr IPict NULL;
hr = OleCreatePictureIndirect(&picdesc, IID_IPicture,FALSE,(LPVOID *)&IPict);
chlASSERT(SUCCEEDED(hr));
IPersistStreamPtr psp = IPict;
IStreamPtr pStream 0;
ULARGE_INTEGER psz;
psp->GetSizeMax(&psz);
你可能感兴趣的文章
BZOJ 1806 IOI2007 Miners 矿工配餐 动态规划
查看>>
参考例子,学习Func<T, TResult>委托
查看>>
NTFS For Mac 如何简单操作
查看>>
django 生成复杂的 PDF 文件(数据较多时)
查看>>
CodeForces 300C 最短路
查看>>
睡觉被憋醒
查看>>
Java 7 Fork/Join 框架
查看>>
c++中冒号(:)和双冒号(::)的用法
查看>>
dubbo工作原理
查看>>
驱动开发利器Microsoft Windows Driver Kit 7.1.0下载
查看>>
[MongoDB]索引
查看>>
maven_项目的依赖、聚合、继承
查看>>
一个C++类的注释:
查看>>
Winsock IO模型之select模型
查看>>
开发规范
查看>>
vim中tab转为空格
查看>>
Android Studio导入第三方类库的方法(转)
查看>>
union和union all的区别
查看>>
debian attempt to kill init!
查看>>
centos7下使用yum安装mysql
查看>>