GDI映射模式—关于窗口和视口

本文解析了Windows GDI中窗口与视口间的坐标转换原理,重点介绍了MM_LOMETRIC和MM_ISOTROPIC两种映射模式的区别及应用实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

最近在学Windows编程,在学到GDI的时候给映射模式,尤其是窗口和视口之间怎么转换给搞得头大,现在总结一下,希望能给其他人一些帮助。

关于什么是设备坐标,什么是逻辑坐标,还有什么是设备坐标系统很多书上都有,这里就不再赘述,重点只讲一下窗口和视口是怎么转换的。首先讲一下三条规则,这是关键中的关键,一切的变化都由此而来。

(1) 映射模式被定义为从“窗口”(逻辑坐标)到“视口”(设备坐标)的映射。

(2) xViewport=(xWindow-xWinOrg)*xViewExt/xWinExt+xViewOrg

yViewport=(yWindow-yWinOrg)*yViewExt/yWinExt +yViewOrg

(3) 视口的(0,0)指的是客户区的左上角(也可以是屏幕或全窗口),x值向右增加,y值向下增加,这是不会改变的。

我们通过几个例子来解释。

一、 首先我们使用MM_LOMETRIC映射模式,它的x值向右增加,y值向下减小,视口范围是(1024-768),意思是指xViewExt=1024,yViewExt=-768,窗口范围是(3200,2400),意思是xWinExt=3200,yWinExt=2400。这里的范围请只把它理解成一个数,而不要理解成一个区间,因为它只是要用来求xViewExt/xWinExt这样一个比值而已。

一开始的时候,xWinOrg,yWinOrg,xViewOrg,yViewOrg的值都是0

所以当我们调用函数SetPixel(hdc,100,-100,0)时(GDI函数使用的参数是逻辑坐标,通过转换为(xViewportyViewport)找到在客户区的位置)

xViewport=(100-0)*(1024/3200)+0=32

yViewport=(-100-0)*(-768/2400) +0=32

也就是在客户区左上角右数32个像素点,下数32个像素点的位置画点。

而如果调用函数SetPixel(hdc,100,100,0),

yViewport=(100-0)*(-768/2400) +0=-32,对应的点在客户区上方,显示不出来。

因为MM_LOMETRIC模式不能修改范围,只能修改xViewOrgyViewOrg,所以我们可以使用函数SetViewportOrgEx(hdc,0,64,NULL)修改yViewOrg,这样就有

yViewport=(100-0)*(-768/2400) +64=32,即又显示在客户区左上角右数32个像素点,下数32个像素点的位置,直观上看是把坐标系的原点位置往下移动了64个像素点,可以这样想象一下,然而实质上的原因是yViewOrg的改变导致计算结果不同,设备点(0,0)仍然在客户区左上角,这一点一定要理解清楚。

 

二、上面的例子比较简单,为了加深理解,现在我们使用MM_ISOTROPIC模式,这个模式一开始的窗口范围和视口范围和MM_LOMETRIC相同,不同的是在这个模式下我们可以自己改变窗口范围和视口范围,且是各向同性的,也就是水平方向和竖直方向的每一个单位对应的像素点个数相同,。

和上面同样的问题在这个模式下我们有另外一种解决的方法,使用函数SetViewportExtEx(hdc,1024,768,NULL)改变yViewExt,则调用SetPixel(hdc,100,100,0)

yViewport=(100-0)*(768/2400) +0=32,这样又可以正常显示了。

这里要注意一点,改变了yViewExt之后,现在向下的方向成了y值增加的方向,因为yViewExt/yWinExt的值为正,所以点(0,100)和点(0,200)算出来后点(0,200)在点(0,100)下方,而原来向下的方向是y值减小的方向,因为yViewExt/yWinExt的值为负,点(0,-100)和点(0,-200)算出来后点(0,-200)在点(0,-100)的下方,这就是为什么说MM_ISOTROPIC模式x,y值增加的方向可选的原因。

MM_ISOTROPIC模式比较灵活,理解了它之后对于GDI映射就能比较好的理解,最后以构建笛卡尔坐标系为例来分析帮助理解。

(1)SetMapMode(hdc,MM_ISOTROPIC);

(2)SetWindowExtEx(hdc,1000,1000,NULL);

(3)SetViewportExtEx(hdc,cxClient/2,-cyClient/2,NULL);

(4)SetViewportOrg(hdc,cxClient/2,cyCleient/2,NULL);

(2)将窗口大小设定为1000,(3)把视口范围宽度设定为客户区宽度的一半,高度设定为客户区高度的一半的负数,此时xViewExt/xWinExt为正数,yViewExt/yWinExt为负数,整个客户区只可以显示x值为正数,y值为负数的点,相当于坐标系的第四象限。因为要保持各项同性,所以窗口大小1000就对应于客户区高度的一半(因为高度比宽度小)。(4)把xViewOrg设为cxClient/2,把xViewOrg设为cyClient/2,相当于把坐标系的原点移动到客户区中心。这样最终就创建了一个以客户区中心为原点的笛卡尔坐标系,并向各方向延展1000个单位。

到这里应该就差不多了,有不理解的请仔细琢磨一开始讲的那三条规则,以上是个人理解,希望能有帮助,如有错误请指出。

 

### Bash Shell 基础使用指南 #### 什么是 BashBash(Bourne Again SHell)是一种广泛使用的 Unix shell 和命令解释器,最初由 Brian Fox 开发并于 1989 年发布。它是 GNU Project 的一部分,并成为大多数 Linux 发行版默认的 shell 工具[^3]。 #### Bash Shell 的基本特性 Bash 提供了许多内置命令来帮助用户高效操作文件系统和执行脚本任务。一些常见的内置命令包括 `cd`、`exit`、`echo`、`pwd` 和 `history` 等[^2]。这些命令的功能直接存储在 shell 中,无需依赖外部二进制程序即可运行。 以下是几个常用的 Bash 功能及其说明: 1. **路径导航** - 使用 `cd` 命令可以更改当前工作目录。 ```bash cd /path/to/directory ``` 2. **显示当前位置** - 使用 `pwd` 可以打印当前的工作目录路径。 ```bash pwd ``` 3. **查看历史记录** - 使用 `history` 查看之前输入过的命令列表。 ```bash history ``` 4. **退出终端会话** - 输入 `exit` 即可关闭当前的 shell 会话。 ```bash exit ``` 5. **变量管理** - 定义环境变量并将其导出到子进程中。 ```bash export MY_VAR="example value" echo $MY_VAR ``` 6. **条件判断与循环结构** - 支持复杂的逻辑控制语句,例如 if 条件测试以及 while/for 循环。 ```bash if [[ "$VAR" == "value" ]]; then echo "Condition met!" fi for i in {1..5}; do echo "Iteration $i" done ``` 7. **管道与重定向** - 利用管道符 (`|`) 将一个命令的标准输出传递给另一个命令作为标准输入。 ```bash ls | grep ".txt$" ``` - 文件读写可以通过重定向完成: ```bash cat file.txt > output.log # 覆盖模式保存至output.log cat another_file >> output.log # 追加模式追加数据 ``` #### Bash vs Zsh:如何选择? 尽管两者都属于交互式 shell 类型工具,但在灵活性上各有千秋。如果追求跨平台支持度高且稳定性强的选择,则推荐优先考虑 Bash;而当更倾向于个性化定制体验或者希望尝试更多新颖特性的场景下可以选择 Zsh[^1]。 #### 解决常见问题的方法 对于初学者来说,在学习过程中可能会遇到各种各样的疑问点。这里列举了一些典型的例子及解决方案: - 如果发现某些特定字符无法正常解析,请确认是否设置了正确的编码格式; - 当试图调用某个外部应用程序失败时,检查 PATH 是否包含了该应用所在位置; - 对于复杂表达式的调试需求,借助 set -x 参数开启逐条跟踪功能以便定位错误源头。 ```bash set -x your_command_here arg1 arg2 ... ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值