Home  >  Article  >  Backend Development  >  C# GDI+ implements rubber band technology

C# GDI+ implements rubber band technology

高洛峰
高洛峰Original
2016-11-19 11:17:351601browse

There should be many people looking for information in this area. Take a look at what I have done below. It may be helpful to you. I hope so.

In order to implement the rubber band technology, I used two methods:
The first is to use the ControlPaint.DrawReversibleLine (Point start, Point end, Color BackColor) method. The principle: draw within the specified starting point and end point on the screen. A reversible line with a specified background color. Drawing the same line again reverses the results of the method. Drawing lines using this method is similar to inverting an area of ​​the screen, although it provides better performance for a wider range of colors.
It should be noted that the start point and end point here are relative to the screen, so I use the PointTOScreen(Point p) method to convert.
Unfortunately, when the mouse is dragged, the transformation drawn (that is, a line segment, in my research field, I call the line segment with a line segment a transformation) does not have a line segment. In order to draw the transformation, it can only be achieved by redrawing when the left button is pressed (if you don't need the line to pop, just comment out Invalidate() in the MouseDown() method). Because the background color backColor=(a,r,g,b) is used when using the DrawReversibleLine() method, it can automatically invert the color, and redrawing when the left button is pressed requires the background color. Reverse color reversebackColor=(a', r', g', b'), so how to get the reverse color of the background color? What I used is to subtract the original background color r, g, b from 255, while the transparency remains unchanged, that is, a'=a;r'=255-r;g'=255-g;b'=255-b; Then repaint with the brush defined by this color.

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();
        }
    }
}

The second method is to directly use the background color to draw the transformation that needs to be erased when the mouse is dragged, and use the current brush to draw a certain transformation. In this way, the transformation lines drawn when the mouse is dragged appear.

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();          
        }
    }
}

The above two methods both use double buffering technology: first create a bitmap bitmap with the same size as the client area, then use the bitmap to create a temporary drawing surface g3, and then draw the transformation on g3, and then Use the form drawing surface g to draw the bits.
 Anti-aliasing technology: Just one sentence g3.SmoothingMode = SmoothingMode.AntiAlias, but please note that anti-aliasing technology cannot be used when the left button is pressed and the mouse is dragged.

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn