• 转 C# GDI+ 实现橡皮筋技术


    http://www.cnblogs.com/arxive/p/6080085.html

    应该有很多人都在寻找这方面的资料,看看下面我做的,或许对你会有所帮助,但愿如此。

    为了实现橡皮筋技术,我用了两种方法:
        第一种是利用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,但要注意的是在左键按下和鼠标拖动的情况下不能使用抗锯齿技术。

  • 相关阅读:
    环求解点值
    汉诺塔(记录每种路径次数)
    快速排序
    选择排序
    冒泡排序
    桶排序
    异或后最大lowerbit
    计数三角形
    nico和niconiconi
    提高程序设计能力的一般方法
  • 原文地址:https://www.cnblogs.com/xiangxiong/p/6813381.html
Copyright © 2020-2023  润新知