注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

简单代码

寻找代码的灵魂

 
 
 

日志

 
 
关于我

对于本博客内所有原创文章和代码的引用必须标明“来源:http://simplesource.blog.163.com/”。如需应用于商业目的必须经本人同意,本人对所有原创文章和代码保留一切权利。 PS:需要部分程序源代码的请留下邮箱地址

网易考拉推荐

放大镜程序原理  

2007-01-22 11:27:18|  分类: 我的程序 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

windows自带了一个帮助视觉障碍人士的放大镜程序,还是比较好玩的。该程序只是将屏幕的画面进行放大,却没有真实放大镜的效果。真实的放大镜由于固有的特性限制,不能保证每处的放大率一致,但在我看来正是这一点才是真实放大镜的魅力所在。

放大镜程序原理

程序效果图:

放大镜程序原理 - 简单代码 - 简单代码

  我采用MFC对话框程序框架构建放大镜程序。我们要达到的效果:截取屏幕图像,对图像进行扭曲,绘制图像,最后切割窗口为圆形。

  1、截取屏幕图像。

  为了截取屏幕图像,我们需要获取屏幕设备上下文(CDC),这一步可以通过::GetWindowDC(NULL)实现。这时我们得到了整个桌面设备上下文的句柄,是HDC类型的,操作起来还很不方便。别急,虽然对于HDC的操作不方便,可是通过它已经可以得到我们所需的所有信息了,我们可以根据这个句柄建立一个CDC的对象,方便我们操作桌面的设备上下文。实现代码如下
 CDC desktop;
 if(!desktop.Attach(::GetWindowDC(NULL)))
 {
  return;
 }

  这样我们的操作就方便了。取得设备上下文不是我们的最终目的,我们需要把整个屏幕的图像信息保存到一个结构中。为了方便以后的操作,我们可以把它放在一个char型的数组中,当然你也可以把它放在一个CBitmap对象中,但是经过我的试验,操作CBitmap对象的速度不如直接char型数组。所以还是选择第一种方法,实际上是把位图操作转换成对内存的直接操作。

  那么要如何把桌面位图信息保存到一个字符数组缓冲里面呢?我采用的方法是,先把位图信息保存到一个CBitmap对象中,然后再提取里面的数据到字符数组。实现代码如下:

CDC cap;
 cap.CreateCompatibleDC(&desktop);
 CBitmap bmp;
 bmp.CreateCompatibleBitmap(pDC, iDesktopCX, iDesktopCY);
 cap.SelectObject(&bmp);
 cap.BitBlt(0, 0, iDesktopCX, iDesktopCY,
  &desktop, 0, 0, SRCCOPY);
 bmp.GetBitmapBits(iDesktopCX * iDesktopCY * iColor,// 缓冲区大小
  buffer);// 缓冲区指针

  这里iDesktopCX,iDesktopCY是桌面的宽度和高度,iColor是颜色数。 需要解释一下颜色数的概念,屏幕的颜色是有数量的,现在的电脑一般为32位,相当于4个字节,iColor就是4,也有256色的,其实是用一个字节存储了一个像素点的颜色,iColor为1。颜色数的判断可以通过CBitmap对象的GetBitmap函数取得颜色信息,然后算出,过程比较复杂,而且不是本文的重点,这里就不说了。

    2、图像扭曲

    图像扭曲的方法有许多种,我尝试了两种方法。由于凸透镜的成像效果是中间放大率大,周围放大率小,所以我尝试用多个不同放大率的圆形贴图实现。首先用放大率为1.1 的圆形拷贝图像到设备上下文,然后再用放大率为1.5的圆形拷贝图像到设备上下文,以此类推,原理图大致如下:

    放大镜程序原理 - 简单代码 - 简单代码

    圆形贴图以及缩放贴图的方法在许多地方都有介绍。圆形贴图的原理是用一个遮罩;缩放贴图就是调用CDC::StretchBlt函数,这些都没什么好说的。

    问题在这种方法的效果。虽然这种方法原理简单直白,实现起来也没有什么麻烦,但是产生的效果实在令人不敢恭维,看上去像高度近视眼镜,可以看到明显一圈一圈的痕迹,非常难看。虽然你可以用增加圆圈的个数来增加图像的精细程度,但是看上去还是极不自然,与实际放大镜的效果还是有很大区别。

    于是我尝试了另一种算法:扭曲矩阵算法。原理更加简单,用一个矩阵表示要扭曲图像中的每一个点应该显示的位置,再根据计算得来的位置进行逐点绘制。原理图如下:

    放大镜程序原理 - 简单代码 - 简单代码

    上图仅作为一种参考,实际程序中扭曲矩阵是通过函数计算而得。而且这是一种理想的算法,这个算法应用到实际程序中有一个漏洞:虽然屏幕上已经有了图像,但是会产生一些黑点。这些小黑点是怎么来的呢?原因很简单,由于图像的有些地方被放大了,所以图像的现有像素数量比要显示的像素数量小,导致有些地方没有被像素覆盖(扭曲矩阵只是把一个像素拷贝到屏幕的一个像素位置,相当于移动了像素),如下图:

    放大镜程序原理 - 简单代码 - 简单代码

    解决这个bug的方法是:逆向思考,上面的扭曲矩阵保证了遍历原图中的所有像素,如果我把原图和显示图像的位置互换,那么不是能够保证遍历到显示图像中的所有像素了吗?这样显示图像就不会有黑点了。矩阵的形式还是没有变,只是矩阵中的点所表示含义发生了改变:矩阵中的每个点表示屏幕上特定位置所需要显示的像素在原图中的位置。

    3、绘制图像

    有了上述的扭曲矩阵,就可以对图像进行扭曲了。你可以逐点描绘到显示设备上下文中,所用的函数是CDC::SetPixel。当然这样做是以你的pc有非常高的配置为前提的,一般情况下使用这样效率低下的直接绘点技术会产生令人无法忍受的频闪和占用100%的cpu。

    这时我们前文把图像的数据拷贝到一个缓冲区的做法就要显示威力了。为何要作这么复杂的工作的原因在于降低cpu的占用率,消除频闪。因为cpu和显示设备进行一次交互很慢,所以我们要尽量减少cpu与显示设备交互的次数。CDC::BitBlt是比较快的一种交互方式。我们先把要显示的图像在内存中生成,然后再用CDC::BitBlt拷贝到设备上下文中。

    我们需要得到原图中每个点的颜色值,这个可以通过颜色数和图像的行列宽度计算得到。

    4、将窗口切割成圆形

    这个在网上的例子比较多,一般是使用CWnd::SetWindowRgn函数,具体使用方法可查询MSDN。

    至此实现这个程序已经没有什么重大的难点,只要下功夫就可以实现,所以不再赘述了。

 

下载地址

以前的链接失效了,要的童鞋请留下邮箱地址。

  评论这张
 
阅读(2931)| 评论(16)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018