手頭目前的一個項目(.Net4.0)中有這樣一個需求:在程序中要進行視頻采集,並且要在視頻影像區域進行繪圖編輯,對繪圖能進行拉伸,拖拽和刪除。從需求來看,必須得在視頻影像區的上方蓋一層畫布才能這么操作了。
首先是找視頻成像控件,在網上找了一圈,找到AForge(http://www.aforgenet.com/framework/downloads.html),寫了個測試代碼,直接用辦公用的筆記本攝像頭進行視頻采集,發現還不錯,DevExpress也有一個視頻控件,不過那個設置起來比較麻煩,而且我們也不需要用那么多附加功能,只要能夠輸出影像就可以。
然后就開始折騰視頻層的上一層了。一開始想法挺簡單,panel不就是可以透明的嗎,找了網上的一個方法,對panel進行透明設置:
BackColor=Color.Transparent
設置完成后調試,哦喲,果然是透明的,然后興沖沖的把視頻控件開啟,悲劇的發現那層白花花的panel擋了,看來此法不通。
然后仔細研究了一下Winform的透明機制,控件的BackColor應該是一個“靜態”屬性,是在重繪的時候進行顏色的傳遞,達到透明的目的,其實是把Panel做成了完美的“變色龍”,而視頻流應該是無法通過這種方式透傳的(不知道我的理解對不對),而我們要的是一塊玻璃,因此這種方式不能達到目的。
然后又開始了各種google,網上找到另一種方式的透明:控制重繪。具體參看 [http://my.oschina.net/HenuToater/blog/520649]。
結果還是沒法把視頻流透傳上來。
這下抓瞎了,病急亂投醫,開始嘗試各種方法,而網上能找到的都是第一種方式。
后來找到一個Demo,是利用Form來做這個透明的,因為Form本身就有透明屬性:Opacity,把這個屬性調到1以下,就能產生透明效果。
OK,就用Form的透明吧,然后緊接着第二個問題來了,怎么蓋到目標控件上面呢?
一開始也是各種瞎試,后來找到一個老司機帶路
Form a = new Form(); a.Show(this); //設置a的Location
然后再監聽主窗體的移動事件,基本上就可以了。
接着,我又興高采烈的開始往下做,在這層Form上畫了幾個圖形,慘烈的發現畫出來的圖形也是透明的,顏色非常淡。這個就是Form透明設置的結果吧——一透到底,上面的控件什么的都透明了,這個可不是我想要的。
繼續在網上瞎幾把找(Winform開發真是累),后來找到了一個商業庫(DSkin),國人開發的,價格也算良心,跟作者溝通了自己遇到的問題,作者表示自己的庫能解決這個問題,於是花了199大洋買了授權,(以下內容為安利DSkin)看了一下DSkin做得還算不錯的,整個控件庫看下來,大多是針對特效這塊做的,作者對Winform應該是非常通透的。
買了授權后,開始心急火燎的寫測試程序,在和作者一輪溝通交流后,順利的解決了這個折騰我2天的東西。
代碼:
GlassDraw.cs 部分
public partial class GlassDrawer : DSkinForm { private Image _Backup; public GlassDrawer() { FormBorderStyle = FormBorderStyle.None; BackColor = Color.Transparent; DoubleBuffered = true; ShowInTaskbar = false; ShowSystemButtons = false; DrawIcon = false; ShowIcon = false; EnableAnimation = false; Text = string.Empty; InitializeComponent(); _Backup = new Bitmap(this.Width, this.Height); _LastLocation = Location; } public Func<GlassDrawer,bool> MoveAssert { get;set; } public void Draw(Action<Graphics> drawer) { try { Graphics g = Graphics.FromImage(_Backup); drawer(g); g.Dispose(); Invalidate(); } catch(Exception e) { } } protected override void OnLayeredPaint(PaintEventArgs e) { if (_Backup != null) { e.Graphics.DrawImage(_Backup, 0, 0); } } protected override void OnMove(EventArgs e) { base.OnMove(e); } private Point _LastLocation; protected override void OnLocationChanged(EventArgs e) { if (MoveAssert != null) { if (!MoveAssert(this)) { Location = _LastLocation; return; } } _LastLocation = Location; base.OnLocationChanged(e); } private Point _TargetLocation; private Control _Target; public void Follow(Control target) { if(_Target != null) { _Target.LocationChanged -= Target_LocationChanged; } _Target = target; target.LocationChanged += Target_LocationChanged; _TargetLocation = PointToScreen( target.Location); } private void Target_LocationChanged(object sender, EventArgs e) { var p = PointToScreen(_Target.Location); } }
Form1.cs 部分
InitializeComponent();
gd.Width = cameraFrame1.Width; gd.Height = cameraFrame1.Height; gd.Location = PointToScreen(cameraFrame1.Location); gd.Text = string.Empty; gd.MoveAssert = (g) => { var srcp = PointToScreen(g.Location); int top = srcp.Y; int left = srcp.X; int right = srcp.X + g.Width; int bottom = srcp.Y + g.Height; var targetp = PointToScreen(this.Location); if (top < targetp.Y || left < targetp.X || right > targetp.X + this.Width || bottom > targetp.Y + this.Height) return false; return true; }; gd.Show(this); cameraFrame1.Start();
