如何在MFC中以毫米(mm)为单位获取视图并设置绘图缩放?
嘿,这个问题我之前做MFC绘图项目的时候也踩过坑——默认情况下MFC把画笔宽度当成像素单位处理,要实现输入毫米值就画出对应物理宽度的线条,核心就是搞定物理单位(毫米)和设备单位(像素)的转换,下面给你拆解两种靠谱的实现方式,还有怎么获取缩放比例:
方法一:手动计算单位转换(适合不想改映射模式的场景)
这种方式不需要修改默认的MM_TEXT映射模式(单位为像素),直接通过DPI(每英寸像素数)计算毫米到像素的转换因子:
获取设备的DPI
用CDC::GetDeviceCaps函数获取当前设备(屏幕/打印机)的水平和垂直DPI,因为有些高分屏或打印机的宽高DPI可能不一样:int dpiX = pDC->GetDeviceCaps(LOGPIXELSX); // 水平方向每英寸像素数 int dpiY = pDC->GetDeviceCaps(LOGPIXELSY); // 垂直方向每英寸像素数计算转换因子
因为1英寸=25.4毫米,所以每毫米对应的像素数就是DPI / 25.4。比如要绘制20mm宽的线条,转换后的像素宽度为:double targetWidthMM = 20.0; // 计算像素宽度,加0.5是为了四舍五入到整数(画笔宽度必须是整数) int penWidthPixel = static_cast<int>(targetWidthMM * (dpiX / 25.4) + 0.5);创建画笔并绘制
用转换后的像素宽度创建画笔,之后正常绘制即可:CPen solidPen; solidPen.CreatePen(PS_SOLID, penWidthPixel, RGB(0, 0, 0)); CPen* pOldPen = pDC->SelectObject(&solidPen); // 绘制示例线条 pDC->MoveTo(50, 50); pDC->LineTo(400, 50); // 恢复原有画笔,避免资源泄漏 pDC->SelectObject(pOldPen); solidPen.DeleteObject();
方法二:使用MFC内置的物理映射模式(更简洁)
MFC提供了几种支持物理单位的映射模式,比如MM_LOMETRIC(单位为0.1毫米,Y轴向上)和MM_HIMETRIC(单位为0.01毫米,精度更高),直接切换映射模式后,画笔宽度可以直接用毫米对应的单位值:
切换映射模式
先保存原有映射模式,再设置为MM_LOMETRIC:int oldMapMode = pDC->SetMapMode(MM_LOMETRIC);创建对应宽度的画笔
20mm等于200个0.1毫米单位,所以画笔宽度设为200即可:CPen thickPen; thickPen.CreatePen(PS_SOLID, 200, RGB(0, 0, 0)); CPen* pOldPen = pDC->SelectObject(&thickPen);注意坐标调整
因为MM_LOMETRIC的Y轴方向和默认MM_TEXT相反(向上为正),所以绘制坐标需要把Y值取负:// 原来的(50,50)要改成(50, -50),保证线条位置和预期一致 pDC->MoveTo(50, -50); pDC->LineTo(400, -50);恢复原有映射模式
绘制完成后记得切换回原来的映射模式:pDC->SelectObject(pOldPen); pDC->SetMapMode(oldMapMode); thickPen.DeleteObject();
如何获取当前的毫米-像素缩放比例
如果需要把已有的像素宽度转换回毫米,或者获取当前设备的缩放系数,反过来计算即可:
// 获取当前画笔的像素宽度 CPen* pCurrentPen = pDC->GetCurrentPen(); int penWidthPixel = pCurrentPen->GetWidth(); // 计算像素转毫米的因子 int dpiX = pDC->GetDeviceCaps(LOGPIXELSX); double pixelToMM = 25.4 / dpiX; // 转换为毫米宽度 double widthMM = penWidthPixel * pixelToMM;
小贴士:如果是打印场景,一定要用打印DC(
CPrintDialog获取的DC)来获取DPI,这样转换出来的宽度在打印纸上才会是精确的20mm,和屏幕显示的可能不一样哦。
内容的提问来源于stack exchange,提问作者Vikranth N

