已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也

Win32/GDI编程中的DPI

打开高DPI的方法

与.NET的方法相同,所不同的是Win32编程中不需要手动引入GetModuleHandle和GetProcAddress两个WinAPI。

<code class="language-cpp">HMODULE hUser32 = GetModuleHandleA("user32");
if (hUser32)
{
	FARPROC pSetProcessDpiAware = GetProcAddress(hUser32, "SetProcessDPIAware");
	if (pSetProcessDpiAware)
	{
		pSetProcessDpiAware();
	}
}
</code>

获取系统DPI的方法

和.NET不同的是,Win32并不会给我们自动缩放,大部分情况下我们需要手动缩放。这时就需要

首先通过GetDC(NULL)获取屏幕的HDC,然后GetDeviceCaps(hscreendc, LOGPIXELSX)获取X方向的DPI,使用GetDeviceCaps(hscreendc, LOGPIXELSY)获取Y方向的DPI,一般来说,它们是相等的,获取完毕,务必使用ReleaseDC(NULL, hscreendc)释放屏幕的HDC。

<code class="language-cpp">int dpix = 96;
int dpiy = 96;

HDC hscreendc = GetDC(NULL);
if (hscreendc)
{
	dpix = GetDeviceCaps(hscreendc, LOGPIXELSX);
	dpiy = GetDeviceCaps(hscreendc, LOGPIXELSY);
	ReleaseDC(NULL, hscreendc);
}
</code>

使用MulDiv进行手工缩放

一般来说,我们可以使用MulDiv进行缩放:

<code class="language-cpp">RECT rc = { 0, 0, 320, 240 };
rc.left = MulDiv(rc.left, dpix, 96);
rc.right = MulDiv(rc.right, dpix, 96);
rc.top = MulDiv(rc.top, dpiy, 96);
rc.bottom = MulDiv(rc.bottom, dpiy, 96);
</code>

MulDiv只适用于整数,对于浮点数来说,还是要使用x * dpix / 96.0f和y * dpiy / 96.0f。

使用AdjustWindowRect调整窗口大小

CreateWindow要求窗口大小,但是我们通常期望设置客户区大小,这时候就要使用AdjustWindowRect了。注意AdjustWindowRect不能预知菜单栏有没有被折行,因此如果菜单项很多的话,不一定能计算出准确的窗口大小。

以下是一个经过DPI缩放和AdjustWindowRect计算的后调用CreateWindows的程序:

<code class="language-cpp">AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, TRUE);

hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL);
</code>

关于GDI+

与WinForms第二种方法基本相同,不同的是,Win32版本的GDI+没有DrawImageUnscaled,需要使用g.DrawImage(&bmp, 0, 0, XXXXXXtWidth(), XXXXXXtHeight());这种用法。

这里有个坑,获得HDC之后必须第一时间创建Graphics对象,不能GDI和GDI+混用,不然GetLastStatus()会返回3,即OutOfMemory,无法使用GetDpiX()和GetDpiY()函数。

关于GDI

由于GDI年代比较久远,不提供浮点数计算,因此不能想当然的使用MM_LOENGLISH或MM_HIENGLISH。比较科学的方法是使用MM_TWIPS。它与DIP之间的换算关系如下:

TWIPS.x = DIP.x * 20

TWIPS.y = DIP.y * -20

由于GDI对除MM_TEXT以外的单位都使用Y轴向上的坐标系,因此比较蛋疼的是,y需要取负才能表示显示在屏幕上的点。

<code class="language-cpp">case WM_PAINT:
	{
		HDC hdc = BeginPaint(hWnd, &ps);
		SetMapMode(hdc, MM_TWIPS);
		MoveToEx(hdc, 10 * 20, 10 * -20, NULL);
		LineTo(hdc, 100 * 20, 100 * -20);
		EndPaint(hWnd, &ps);
		return 0;
	}
</code>

此外,TWIPS与Pixel的转换不需要手工进行,只需要使用LPtoDP和DPtoLP即可。

<code class="language-cpp">RECT rc = { 10 * 20, 10 * -20, 300 * 20, 200 * -20 };
LPtoDP(hdc, (LPPOINT)&rc, 2);
</code>

运行效果:

Win32LowDpi.png

Win32HighDpi.png

文号 / 827698

千古风流
名片发私信
学术分 4
总主题 466 帖总回复 2942 楼拥有证书:进士 学者 笔友
注册于 2009-05-30 21:22最后登录 2019-01-31 17:16
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步

个人简介

暂未填写
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

插入资源
全部
图片
视频
音频
附件
全部
未使用
已使用
正在上传
空空如也~
上传中..{{f.progress}}%
处理中..
上传失败,点击重试
等待中...
{{f.name}}
空空如也~
(视频){{r.oname}}
{{selectedResourcesId.indexOf(r.rid) + 1}}
处理中..
处理失败
插入表情
我的表情
共享表情
Emoji
上传
注意事项
最大尺寸100px,超过会被压缩。为保证效果,建议上传前自行处理。
建议上传自己DIY的表情,严禁上传侵权内容。
点击重试等待上传{{s.progress}}%处理中...已上传,正在处理中
空空如也~
处理中...
处理失败
加载中...
草稿箱
加载中...
此处只插入正文,如果要使用草稿中的其余内容,请点击继续创作。
{{fromNow(d.toc)}}
{{getDraftInfo(d)}}
标题:{{d.t}}
内容:{{d.c}}
继续创作
删除插入插入
插入公式
评论控制
加载中...
文号:{{pid}}
加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}
ID: {{user.uid}}