天天看点

(开源,GPLv3)EbookCopier 实现(2) – 具有半透明和鼠标穿透效果的“取景框”

设置剪辑区域后,程序会在屏幕上显示“取景框”标出剪辑区域,如下图所示,图中红色边框和和中间的“剪辑区域”字样即是。这个“取景框”像贴在屏幕上一样,不会影响你键盘和鼠标的操作。它事实上是由一个分层窗口(a layered window)实现的。

(开源,GPLv3)EbookCopier 实现(2) – 具有半透明和鼠标穿透效果的“取景框”

一、窗体的阿尔法混合透明(Per-Pixel-Alpha)效果

这一实现必须调用 Win32 API,这意味着我们的程序只能在 Windows 下运行,别指望随 Mono 去 Linux 兜风了。

很高兴的是,在我动手之前看到了 Rui Lopes 为此写的 Per Pixel Alpha Blend in C#,已经实现了这一效果的托管封装,我们的窗体从中派生就可以了。这些代码在项目的 /EbookCopier/Ref/PerPixelAlphaForm.cs 文件中。尽管如此,我们还是看一下实现的原理吧。

Windows 2000操作系统增加了 WS_EX_LAYERED 扩展窗口风格。如果使用了该属性,窗体将具备复合形状、动画、阿尔法混合等方面的视觉特效。为得到一个分层窗口(layered window),必须设置 WS_EX_LAYERED 标志位,这可以在窗体创建时设置,也可以在创建后通过以GWL_EXSTYLE标志调用 SetWindowLong() 来进行设置。在托管代码中可以这样实现:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style
        return cp;
    }
}      

接下来,可以通过 UpdateLayeredWindows() 函数来更新分层窗口。在具体使用时,需要在位图中绘制出可视区域,并将其与关键色、阿尔法混合参数等一起提供给 UpdateLayeredWindows() 函数。托管代码中需要 DllImport,封装如下:

public const Int32 ULW_COLORKEY = 0x00000001;
public const Int32 ULW_ALPHA = 0x00000002;
public const Int32 ULW_OPAQUE = 0x00000004;

/// 
/// The UpdateLayeredWindow function updates the position, size, shape, content,
/// and translucency of a layered window. 
/// 
/// 
窗体句柄
/// 
窗体的 DC 句柄
/// 
窗体左上角位置
/// 
窗体大小
/// 
要设置的图像源 DC 句柄
/// 
图像源位置
/// 
要透明的颜色
/// 
窗体透明度
/// 
标识位,ULW_COLORKEY 为指定颜色透明,ULW_ALPHA 为允许半透明的窗体,ULW_OPAQUE 为不透明
/// 成功=True; 失败=False;
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool UpdateLayeredWindow(
    IntPtr hwnd,
    IntPtr hdcDst,
    ref Point pptDst,
    ref Size psize,
    IntPtr hdcSrc,
    ref Point pprSrc,
    Int32 crKey,
    ref BLENDFUNCTION pblend,
    Int32 dwFlags);      

二、窗体的鼠标穿透(Click-Through)效果

具有WS_EX_LAYERED 标志的分层窗口,添加 WS_EX_TRANSPARENT 就可以使鼠标穿透。该实现位于 /EbookCopier/AlphaForm.cs 中。我添加了一个 ClickThroughEnable 属性,设置为True,则窗体具有鼠标穿透效果。

private bool clickThroughEnable = false;
/// 
/// 获取或设置一个值,指示窗体是否有鼠标穿透功能。
/// 
/// 如果该实例can penetrate,为true;否则为false。
public bool ClickThroughEnable
{
    get { return clickThroughEnable; }
    set
    {
        clickThroughEnable = value;

        if (clickThroughEnable)
        {//使窗口有鼠标穿透功能
            uint intExTemp = GetWindowLong(this.Handle, GWL_EXSTYLE);
            uint oldGWLEx = SetWindowLong(this.Handle, GWL_EXSTYLE, intExTemp | WS_EX_TRANSPARENT | WS_EX_LAYERED);
        }
        else
        {//使窗体恢复正常
            this.FormBorderStyle = FormBorderStyle.None;
        }
    }
}      

三、GDI+ 绘图

在实现阿尔法混合透明和鼠标穿透功能后,设置窗体TopMost=true和ShowInTaskbar=false,就得到了我们想要的窗体。接下来GDI+绘制我们想要的图形就可以了。

“取景框”窗体类(class FocusRectForm)在 /EbookCopier/FocusRectForm.cs 中实现,它从 AlphaForm 类派生。根据捕捉状态的不同,它共有四种样子,正常模式如下:

(开源,GPLv3)EbookCopier 实现(2) – 具有半透明和鼠标穿透效果的“取景框”

连续捕捉时,显示当前页面:

(开源,GPLv3)EbookCopier 实现(2) – 具有半透明和鼠标穿透效果的“取景框”

截取屏幕图像时,需要剪辑区域空白:

(开源,GPLv3)EbookCopier 实现(2) – 具有半透明和鼠标穿透效果的“取景框”

截取屏幕后,显示半透明蒙层,以表示已经拷贝完毕:

(开源,GPLv3)EbookCopier 实现(2) – 具有半透明和鼠标穿透效果的“取景框”

具体代码比较繁琐,就不贴在文章中了,有兴趣的话请浏览 /EbookCopier/FocusRectForm.cs 文件。

至此,我们就实现了这个“取景框”窗体。

数字图书复印机(EbookCopier) 遵循 GPLv3 协议开源,如果你有更多的创意,欢迎请加入我们。

项目地址:http://code.google.com/p/ebookcopier/

源代码和可执行程序下载:http://code.google.com/p/ebookcopier/downloads/list

本文同时发布到以下位置,以便更多的朋友能够看到本文:

http://chuangen.name/ (我的主页)

http://blog.csdn.net/chuangen (IT 社区)

http://chuangen.cnblogs.com/ (.NET 技术社区)

http://chuangen-cn.spaces.live.com/ (live Spaces)

继续阅读