應該有很多人都在尋找這方面的資料,看看下面我做的,或許對你會有所協助,但願如此。
為了實現橡皮筋技術,我用了兩種方法:
第一種是利用ControlPaint.DrawReversibleLine(Point start,Point end, Color BackColor)方法,原理:在螢幕上指定的起始點和結束點內繪製具有指定背景色的可逆線,再次繪製同一條線會逆轉該方法的結果。使用該方法繪製線類似於反轉螢幕的一個地區,不過它提供了更好的效能適用於更廣泛的顏色。
要注意的是這的start起始點和end終止點是相對於螢幕的,因此我用PointTOScreen(Point p)方法進行轉換。
遺憾的是,在滑鼠拖動的時候,畫出來的變換(即一段線段,在我的研究領域內,我稱帶線冒的線段為變換)不帶線冒。為了畫出變換,只有採用在左鍵按下時重畫來實現(如果你不需要線冒,把MouseDown()方法中的Invalidate()注釋掉就行了)。因為在採用DrawReversibleLine()方法時用的是背景色backColor=(a,r,g,b),它能自動對顏色進行反轉,而採用在左鍵按下時重畫就需要用背景色的反轉顏色reversebackColor=(a’,r',g',b'),那麼怎樣擷取背景色的反轉顏色呢?我採用的是用255減原來的背景色的r,g,b,而透明度不變,即a'=a;r'=255-r;g'=255-g;b'=255-b;然後用這種顏色定義的畫筆來重畫。
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;using System.Drawing.Drawing2D;//包含這個進階二維圖形命名空間namespace ReverseLines{ public partial class Form1 : Form { public Form1() { InitializeComponent(); //啟用雙緩衝技術 SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); SetStyle(ControlStyles.DoubleBuffer, true); } private Point[][] tranGroup = new Point[1000][];//變換組 private int tranNumb = 0;//變換序號 private int pushNumb = 0;//左鍵按下情況:0為開始畫變換,1為結束 private Point curP;//儲存變換時滑鼠的當前點 private Point startP, oldP;//變換的起點和滑鼠移動時的當前點 private Graphics g0,g3;//視窗繪圖面和採用雙緩衝時的臨時繪圖面 private Point endPoint;//儲存右鍵按下時放棄繪製相連變換的滑鼠點 private Color clr,clr1;//擷取表單背景色和反轉背景色 private Pen p;//重畫變換時所用的筆 private Bitmap bitmap = null;//雙緩衝時用的位元影像 private void Form1_Paint(object sender, PaintEventArgs e) { g0 = e.Graphics; bitmap = new Bitmap(ClientSize.Width, ClientSize.Height);//建立臨時位元影像 g3 = Graphics.FromImage(bitmap);//從位元影像建立繪圖面 g3.Clear(this.BackColor);//清除背景色 g3.SmoothingMode = SmoothingMode.AntiAlias;//設定消除鋸齒平滑模式 //在臨時位元影像上重畫變換,消除鋸齒,帶線冒 for (int i = 0; i < tranNumb; i++) { g3.DrawLine(p, tranGroup[i][0], tranGroup[i][1]); } //把臨時位元影像拷貝到表單繪圖面 g0.DrawImage(bitmap, 0, 0); } private void Form1_Load(object sender, EventArgs e) { clr = this.BackColor;//擷取表單背景色 clr1 = Color.FromArgb(clr.A, 255 - clr.R, 255 - clr.G, 255 - clr.B);//反轉背景色 p = new Pen(clr1, 1);//定義滑鼠左鍵按下並移動時繪製變換所用的筆 //自訂線冒 AdjustableArrowCap cap = new AdjustableArrowCap(3, 3); cap.WidthScale = 3; cap.BaseCap = LineCap.Square; cap.Height = 3; p.CustomEndCap = cap; //迴圈繪製變換組中的變換 for (int i = 0; i < 1000; i++) { tranGroup[i] = new Point[2]; } } private void Form1_MouseDown(object sender, MouseEventArgs e) { Graphics g2 = CreateGraphics(); //判斷變換數 if (tranNumb >= 999) { pushNumb = 0; Capture = false; return; } //左鍵按下 if (e.Button == MouseButtons.Left) { if (pushNumb == 0)//判斷是否是折線的開始 { if (endPoint.X != e.X || endPoint.Y != e.Y) { pushNumb++; startP.X = e.X; startP.Y = e.Y; oldP.X = e.X; oldP.Y = e.Y; Capture = true;//捕獲滑鼠 } } else if (pushNumb == 1)//如果不是一段新的折線的開始 { ControlPaint.DrawReversibleLine(PointToScreen(startP), PointToScreen(new Point(e.X, e.Y)), clr); ControlPaint.DrawReversibleLine(PointToScreen(startP), PointToScreen(new Point(e.X, e.Y)), clr); //把變換存入變換組中 curP.X = e.X; curP.Y = e.Y; tranGroup[tranNumb][0] = startP; tranGroup[tranNumb][1] = curP; tranNumb++; startP.X = e.X; startP.Y = e.Y; //儲存一段折線的最後一個點的座標 endPoint.X = e.X; endPoint.Y = e.Y; } } //右鍵按下 if (e.Button == MouseButtons.Right) { //變換數超過變換組最大限度 if (pushNumb == 0) return; //變換數沒有超過變換組最大限度 pushNumb = 0;//一段折線結束 Capture = false;//釋放滑鼠 //繪製最後一個變換 ControlPaint.DrawReversibleLine(PointToScreen(startP), PointToScreen(new Point(e.X, e.Y)), clr); } //失效重畫,為消除鋸齒 Invalidate(); g2.Dispose(); } private void Form1_MouseMove(object sender, MouseEventArgs e) { Graphics g1 = CreateGraphics(); //左鍵按下並移動滑鼠 if (pushNumb == 1) { if (oldP.X != e.X || oldP.Y != e.Y) { //在螢幕上指定的起始點和結束點內繪製具有指定背景色的可逆線 //再次繪製同一條線會逆轉該方法的結果。使用該方法繪製線類似於反轉螢幕的一個地區, //不過它提供了更好的效能適用於更廣泛的顏色。 ControlPaint.DrawReversibleLine(PointToScreen(startP), PointToScreen(oldP), clr); ControlPaint.DrawReversibleLine(PointToScreen(startP), PointToScreen(new Point(e.X, e.Y)), clr); //儲存一個變換的終點,作為下一變換的起點 oldP.X = e.X; oldP.Y = e.Y; } } g1.Dispose(); } //釋放資源 private void Form1_FormClosed(object sender, FormClosedEventArgs e) { g3.Dispose(); bitmap.Dispose(); g0.Dispose(); } }}
第二種是直接利用背景色來繪製滑鼠拖動時需要被擦除的變換,而用當前畫筆來繪製一個確定的變換。採用這種方式可以使滑鼠被拖動時畫出來的變換帶線冒。
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;using System.Drawing.Drawing2D;namespace Shiqu2{ public partial class Form1 : Form { public Form1() { InitializeComponent(); //啟用雙緩衝技術 SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); SetStyle(ControlStyles.DoubleBuffer, true); } private Point[][] tranGroup = new Point[1000][];//變換組 private int tranNumb = 0;//變換序號 private int pushNumb = 0;//左鍵按下情況:0為開始畫變換,1為結束 private Point curP;//儲存變換時滑鼠的當前點 private Point startP, oldP;//變換的起點和滑鼠移動時的當前點 private Graphics g0, g3;//視窗繪圖面和採用雙緩衝時的臨時繪圖面 public Pen curPen;//一個變換確定並要繪製時所用的畫筆 private Point endPoint;//儲存右鍵按下時放棄繪製相連變換的滑鼠點 private Color clr;//擷取表單背景色 private Pen p;//重畫變換時所用的筆 private Bitmap bitmap = null;//雙緩衝時用的位元影像 private void Form1_Paint(object sender, PaintEventArgs e) { g0 = e.Graphics; bitmap = new Bitmap(ClientSize.Width, ClientSize.Height);//建立臨時位元影像 g3 = Graphics.FromImage(bitmap);//從位元影像建立繪圖面 g3.Clear(this.BackColor);//清除背景色 g3.SmoothingMode = SmoothingMode.AntiAlias;//設定消除鋸齒平滑模式 //在臨時位元影像上重畫已有的變換,消除鋸齒,帶線冒 for (int i = 0; i < tranNumb; i++) { g3.DrawLine(curPen, tranGroup[i][0], tranGroup[i][1]); } //把臨時位元影像拷貝到表單繪圖面 g0.DrawImage(bitmap, 0, 0); } private void Form1_Load(object sender, EventArgs e) { curPen = new Pen(Color.Black, 1);//定義一個變換確定並要繪製時所用的畫筆 clr = this.BackColor;//擷取表單背景色 p = new Pen(clr, 1);//定義滑鼠移動是重畫所以的畫筆 //自訂線冒 AdjustableArrowCap cap = new AdjustableArrowCap(3, 3); cap.WidthScale = 3; cap.BaseCap = LineCap.Square; cap.Height = 3; curPen.CustomEndCap = cap; p.CustomEndCap = cap; //初始化繪製變換組中的變換 for (int i = 0; i < 1000; i++) { tranGroup[i] = new Point [2]; } } private void Form1_MouseDown(object sender, MouseEventArgs e) { Graphics g2=CreateGraphics (); //判斷變換數 if (tranNumb >= 999) { pushNumb = 0; Capture = false; return; } //左鍵按下 if (e.Button == MouseButtons.Left) { if (pushNumb == 0)//判斷是否是折線的開始 { if (endPoint.X != e.X || endPoint.Y != e.Y) { pushNumb++; startP.X = e.X; startP.Y = e.Y; oldP.X = e.X; oldP.Y = e.Y; Capture = true;//捕獲滑鼠 } } else if (pushNumb == 1)//如果不是一段新的折線的開始 { g2.DrawLine(curPen, startP, new Point(e.X, e.Y)); //把變換存入變換組中 curP.X = e.X; curP.Y = e.Y; tranGroup[tranNumb][0] = startP; tranGroup[tranNumb][1] = curP; tranNumb++; startP.X = e.X; startP.Y = e.Y; //儲存一段折線的最後一個點的座標 endPoint.X = e.X; endPoint.Y = e.Y; } } //右鍵按下 if (e.Button == MouseButtons.Right) { //變換數超過變換組最大限度 if (pushNumb == 0) return; //變換數沒有超過變換組最大限度 pushNumb = 0;//一段折線結束 Capture = false;//釋放滑鼠 } //失效重畫,為消除鋸齒 Invalidate(); g2.Dispose(); } private void Form1_MouseMove(object sender, MouseEventArgs e) { Graphics g1 = CreateGraphics(); //左鍵按下並移動滑鼠 if (pushNumb ==1) { if (oldP .X !=e.X||oldP .Y !=e.Y) { g1.DrawLine(p, startP, oldP);//用背景色繪製原來的變換 g1.DrawLine(curPen, startP, new Point(e.X, e.Y));//用當前畫筆繪製當前變換 //用當前繪製已有的變換,防止它們被擦除 for (int i = 0; i < tranNumb; i++) { g1.DrawLine(curPen, tranGroup[i][0], tranGroup[i][1]); } //儲存一個變換的終點,作為下一變換的起點 oldP.X = e.X; oldP.Y = e.Y; } } } private void Form1_FormClosed(object sender, FormClosedEventArgs e) { //釋放資源 g3.Dispose(); bitmap.Dispose(); g0.Dispose(); } }}
上述兩種方法都採用了雙緩衝技術:先建立一個大小和客戶區一樣的位元影像bitmap,再用位元影像建立一個臨時的繪圖面g3,然後在g3上繪製變換,畫完之後再用表單繪圖面g把位元影像畫出來。
消除鋸齒技術:只需一句話g3.SmoothingMode = SmoothingMode.AntiAlias,但要注意的是在左鍵按下和滑鼠拖動的情況下不能使用消除鋸齒技術。