0

0

C# GDI+ 实现橡皮筋技术

高洛峰

高洛峰

发布时间:2016-11-19 11:17:35

|

1613人浏览过

|

来源于php中文网

原创

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

为了实现橡皮筋技术,我用了两种方法:
    第一种是利用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();
        }
    }
}

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

百度智能云·曦灵
百度智能云·曦灵

百度旗下的AI数字人平台

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

相关专题

更多
Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

37

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

37

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

16

2026.01.13

PHP缓存策略教程大全
PHP缓存策略教程大全

本专题整合了PHP缓存相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.13

jQuery 正则表达式相关教程
jQuery 正则表达式相关教程

本专题整合了jQuery正则表达式相关教程大全,阅读专题下面的文章了解更多详细内容。

3

2026.01.13

交互式图表和动态图表教程汇总
交互式图表和动态图表教程汇总

本专题整合了交互式图表和动态图表的相关内容,阅读专题下面的文章了解更多详细内容。

45

2026.01.13

nginx配置文件详细教程
nginx配置文件详细教程

本专题整合了nginx配置文件相关教程详细汇总,阅读专题下面的文章了解更多详细内容。

9

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 6.7万人学习

C 教程
C 教程

共75课时 | 4万人学习

C++教程
C++教程

共115课时 | 12.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号