C# GDI+ 实现橡皮筋技术

简介:

原文 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();
        }
    }
}
复制代码

 

第二种是直接利用背景色来绘制鼠标拖动时需要被擦除的变换,而用当前画笔来绘制一个确定的变换。采用这种方式可以使鼠标被拖动时画出来的变换带线冒。

复制代码
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,但要注意的是在左键按下和鼠标拖动的情况下不能使用抗锯齿技术。

 

 

 

没有整理与归纳的知识,一文不值!高度概括与梳理的知识,才是自己真正的知识与技能。 永远不要让自己的自由、好奇、充满创造力的想法被现实的框架所束缚,让创造力自由成长吧! 多花时间,关心他(她)人,正如别人所关心你的。理想的腾飞与实现,没有别人的支持与帮助,是万万不能的。


    本文转自wenglabs博客园博客,原文链接:http://www.cnblogs.com/arxive/p/6080085.html ,如需转载请自行联系原作者



相关文章
|
22天前
|
存储 C# 开发者
掌握 C#编程:关键技术与实践
【4月更文挑战第20天】掌握C#编程的关键技术,包括面向对象编程、数据类型、控制流、异常处理、集合泛型、委托事件和异步编程。通过学习基础、实践项目、代码调试、参考优秀代码、社区交流和持续学习来提升技能。注重代码质量,以开发高质量应用程序。
|
1月前
|
数据采集 C# 数据安全/隐私保护
掌握 C# 爬虫技术:使用 HttpClient 获取今日头条内容
本文介绍了如何使用C#的HttpClient与爬虫代理IP技术抓取今日头条内容,以实现高效的数据采集。通过结合亿牛云爬虫代理,可以绕过IP限制,增强匿名性。文中提供了一个代码示例,展示如何设置代理服务器信息、请求头,并用正则表达式提取热点新闻标题。利用多线程技术,能提升爬虫采集效率,为市场分析等应用提供支持。
掌握 C# 爬虫技术:使用 HttpClient 获取今日头条内容
|
9月前
|
C#
45【软件技术基础】C#调用NPOI插件对EXCEL进行处理
NPOI插件进行EXCEL表格处理,不依赖第三方软件,功能强大,使用简便。 C#调用NPOI插件进行EXCEL单个表和文件夹中批量表的处理。
142 1
|
7月前
|
Oracle 关系型数据库 API
C# LIS检验系统源码,接口技术:RESTful API + Http+WCF
LIS检验系统一种专门用于医院化验室的计算机系统,它致力于提高医院化验室的工作效率和检测准确率。LIS系统由多个子系统组成,包括样本管理系统、质控系统、检验结果管理系统、报告管理系统等。体系结构:Client/Server架构 SaaS模式 客户端:WPF+Windows Forms 服务端:C# +.Net 数据库:Oracle 接口技术:RESTful API + Http+WCF
|
8月前
|
C#
C# GDI+绘图(四)实现网格绘制,并填充相应的表格内容
C# GDI+绘图(四)实现网格绘制,并填充相应的表格内容
|
8月前
|
C#
C# GDI+绘图(三)GDI+实现QQ截图类似功能
C# GDI+绘图(三)GDI+实现QQ截图类似功能
|
8月前
|
C#
C# GDI+绘图(二)进阶---Pen/Brush以及坐标轴平移和旋转等
上一篇C# GDI+绘图(一)GDI+介绍及基础,我们介绍了,GDI+的基础,这篇我们对其进阶内容进行学习,分别为Pen/Brush以及坐标轴操作
|
8月前
|
C# 图形学
C# GDI+绘图(一)GDI+介绍及基础
最近,项目中,有一块比较发杂的网格,并在网格上绘有各种颜色和文本,在Dev库中并未找到能实现这种功能的现有或可以二次开发的控件,因此,涉及到GDI+绘图这块陌生的领域。下面即时我在本次学习过程中的笔记,本次内容一共分为4篇,分别都有各自的代码或工程文件提供,有需要的朋友可以下载。
|
9月前
|
C# 开发者
C# 开发者技术:进程间数据共享之管道(Pipes)-异步通信版
主要类 1.NamedPipeClientStream 2.NamedPipeServerStream 解释:命名管道是一种进程间通信的方式,它允许不同进程之间在同一台机器上进行通信
467 2
C# 开发者技术:进程间数据共享之管道(Pipes)-异步通信版
|
11月前
|
XML 开发框架 JSON
【C#技术相关合集】目录导航
【C#技术相关合集】目录导航
47 0