如何正确绘制不受显示器分辨率影响的6英寸GDI+矩形
要在不同DPI的显示器上精确绘制指定物理长度的图形,需遵循以下步骤:
1. 确认DPI感知模式正确性
你已设置PROCESS_PER_MONITOR_DPI_AWARE和DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2,这是当前推荐的高DPI感知方案——V2模式支持窗口跨显示器移动时的动态DPI调整,无需重启或重新布局。
2. 精确计算物理长度对应像素数
物理长度与像素数的核心关系为:像素数 = 物理长度(英寸) × 当前显示器DPI。直接用GetDpiForWindow(hwnd)获取窗口所在显示器的实时DPI即可。
3. 修正GDI+ Pen的绘制偏移
GDI+的Pen默认采用居中对齐(PenAlignmentCenter),当Pen宽度为1像素时,矩形边框会在路径两侧各延伸0.5像素,导致实际显示的矩形比设置尺寸宽1像素。修正偏移有两种可靠方式:
方式一:设置Pen为内部对齐
将Pen对齐方式改为PenAlignmentInset,让Pen宽度完全绘制在矩形路径内部,不会额外扩展尺寸:
Pen colP(Color(255, 255, 255)); colP.SetAlignment(PenAlignmentInset); auto dpi = GetDpiForWindow(hwnd); int targetWidth = lround(6.0f * dpi); // 6英寸 × DPI = 目标像素宽度 gr.DrawRectangle(&colP, 0, 0, targetWidth, 30);
方式二:调整矩形坐标与尺寸
若不想修改Pen对齐逻辑,可将矩形宽度减1,并将X坐标偏移0.5像素,抵消Pen居中绘制的偏移:
Pen colP(Color(255, 255, 255)); auto dpi = GetDpiForWindow(hwnd); int targetWidth = lround(6.0f * dpi); gr.DrawRectangle(&colP, 0.5f, 0, targetWidth - 1, 30);
4. 确保Graphics对象使用正确DPI
创建Gdiplus::Graphics时,建议直接从窗口句柄创建(Graphics gr(hwnd)),它会自动匹配窗口当前DPI;若必须从HDC创建,需手动设置Graphics的DPI:
HDC hdc = GetDC(hwnd); Graphics gr(hdc); auto dpi = GetDpiForWindow(hwnd); gr.SetDpiX(dpi); gr.SetDpiY(dpi);
你的临时方案2*dpi - USER_DEFAULT_SCREEN_DPI - 2能生效,本质是错误计算刚好抵消了两处偏差:
- Graphics对象DPI不匹配偏差:若你的
Graphics默认使用系统标准DPI(96)而非窗口实时DPI,绘制时会按dpi/96比例自动缩放图形。2*dpi - 96的错误放大倍数,刚好抵消了这个缩放带来的尺寸偏差。 - Pen居中绘制的偏移:减去的数值
2,刚好抵消了Pen居中对齐导致的左右各0.5像素扩展(总偏移1像素),再加上取整误差的修正,最终得到了接近精确的6英寸尺寸。
这种方案完全是误打误撞,没有理论支撑,在不同DPI设置、不同Pen宽度的场景下会失效,建议替换为上述标准实现方式。
内容的提问来源于stack exchange,提问作者poby

