天天看點

C# GDI+ 實作橡皮筋技術(上)

    應該有很多人都在尋找這方面的資料,看看下面我做的,或許對你會有所幫助,但願如此。

為了實作橡皮筋技術,我用了兩種方法:

    第一種是利用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();

        }

    }

}