原文 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[][];//变换组
private int tranNumb = ;//变换序号
private int pushNumb = ;//左键按下情况: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 = ; i < tranNumb; i++)
{
g3.DrawLine(p, tranGroup[i][], tranGroup[i][]);
} //把临时位图拷贝到窗体绘图面
g0.DrawImage(bitmap, , );
} private void Form1_Load(object sender, EventArgs e)
{
clr = this.BackColor;//获取窗体背景色
clr1 = Color.FromArgb(clr.A, - clr.R, - clr.G, - clr.B);//反转背景色
p = new Pen(clr1, );//定义鼠标左键按下并移动时绘制变换所用的笔 //自定义线冒
AdjustableArrowCap cap = new AdjustableArrowCap(, );
cap.WidthScale = ;
cap.BaseCap = LineCap.Square;
cap.Height = ;
p.CustomEndCap = cap; //循环绘制变换组中的变换
for (int i = ; i < ; i++)
{
tranGroup[i] = new Point[];
}
} private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Graphics g2 = CreateGraphics(); //判断变换数
if (tranNumb >= )
{
pushNumb = ;
Capture = false;
return;
} //左键按下
if (e.Button == MouseButtons.Left)
{
if (pushNumb == )//判断是否是折线的开始
{
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 == )//如果不是一段新的折线的开始
{
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][] = startP;
tranGroup[tranNumb][] = 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 == )
return; //变换数没有超过变换组最大限度
pushNumb = ;//一段折线结束
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 == )
{
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[][];//变换组
private int tranNumb = ;//变换序号
private int pushNumb = ;//左键按下情况: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 = ; i < tranNumb; i++)
{
g3.DrawLine(curPen, tranGroup[i][], tranGroup[i][]);
} //把临时位图拷贝到窗体绘图面
g0.DrawImage(bitmap, , );
} private void Form1_Load(object sender, EventArgs e)
{
curPen = new Pen(Color.Black, );//定义一个变换确定并要绘制时所用的画笔
clr = this.BackColor;//获取窗体背景色
p = new Pen(clr, );//定义鼠标移动是重画所以的画笔 //自定义线冒
AdjustableArrowCap cap = new AdjustableArrowCap(, );
cap.WidthScale = ;
cap.BaseCap = LineCap.Square;
cap.Height = ;
curPen.CustomEndCap = cap;
p.CustomEndCap = cap; //初始化绘制变换组中的变换
for (int i = ; i < ; i++)
{
tranGroup[i] = new Point [];
}
} private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Graphics g2=CreateGraphics (); //判断变换数
if (tranNumb >= )
{
pushNumb = ;
Capture = false;
return;
} //左键按下
if (e.Button == MouseButtons.Left)
{
if (pushNumb == )//判断是否是折线的开始
{
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 == )//如果不是一段新的折线的开始
{
g2.DrawLine(curPen, startP, new Point(e.X, e.Y)); //把变换存入变换组中
curP.X = e.X;
curP.Y = e.Y;
tranGroup[tranNumb][] = startP;
tranGroup[tranNumb][] = 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 == ) return; //变换数没有超过变换组最大限度
pushNumb = ;//一段折线结束
Capture = false;//释放鼠标
} //失效重画,为抗锯齿
Invalidate();
g2.Dispose();
} private void Form1_MouseMove(object sender, MouseEventArgs e)
{
Graphics g1 = CreateGraphics(); //左键按下并移动鼠标
if (pushNumb ==)
{
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 = ; i < tranNumb; i++)
{
g1.DrawLine(curPen, tranGroup[i][], tranGroup[i][]);
} //存储一个变换的终点,作为下一变换的起点
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,但要注意的是在左键按下和鼠标拖动的情况下不能使用抗锯齿技术。

C# GDI+ 实现橡皮筋技术的更多相关文章

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

    http://www.cnblogs.com/arxive/p/6080085.html 应该有很多人都在寻找这方面的资料,看看下面我做的,或许对你会有所帮助,但愿如此. 为了实现橡皮筋技术,我用了两 ...

  2. VC++ MFC橡皮筋技术

    在MFC下绘制直线,使用橡皮筋技术,可以使直线效果跟随鼠标移动 //OnLButtionDown        m_ptOrigin = m_ptEnd = point;  //OnMouseMove ...

  3. C# GDI+双缓冲技术

    我想有很多搞图形方面的朋友都会用到双缓冲技术的时候,而且有的时候她的确是个头疼的问题.最近我也要用双缓冲技术,程序怎么调试都不合适,当要对图形进行移动时,总是会出现闪烁抖动.在网上找了些资料,说得都不 ...

  4. GDI+图形图像处理技术中Pen和Brush的简单使用和简单图形的绘制(C#)

    1.Graphics Graphics对象是GDI+绘图表面,因此在Windows窗体应用程序中要使用GDI+创建绘图,必须要先创建Graphics.在给窗体注册一个Paint事件后,Graphics ...

  5. GDI+图形图像处理技术——GDIPlus绘图基础

    GDI+概述 GDI在windows中定义为Graphis Device interface,及图形设备接口,是Windows API(application Programming Interfac ...

  6. GDI+图形图像技术1

    System.Drawing命名空间提供了对GDI+基本图形功能的访问,其中一些子命名空间中提供了更高级的功能. GDI+由GDI发展而来,是Windows图形显示程序与实际物理设备之间的桥梁. GD ...

  7. 在MFC下绘制直线,使用橡皮筋技术,可以使直线效果跟随鼠标移

    void CGraphic1View::OnMouseMove(UINT nFlags, CPoint point) {        if(MK_LBUTTON == nFlags)    {    ...

  8. DXGI快速截屏录屏技术

    DXGI快速截屏录屏技术 概述   很多地方都需要用到截屏/录屏技术,比如桌面直播,桌面录制等等.在微软Windows平台,有很多截屏的接口,不过大多数性能并不理想,Windows8以后微软引入了一套 ...

  9. GDI绘制时钟效果,与系统时间保持同步,基于Winform

    2018年工作之余,想起来捡起GDI方面的技术,特意在RichCodeBox项目中做了两个示例程序,其中一个就是时钟效果,纯C#开发.这个CSharpQuartz是今天上午抽出一些时间,编写的,算是偷 ...

随机推荐

  1. Git学习的网址

    http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 https://github.co ...

  2. spi总结

    SPI的四种模式 SPI的相位(CPHA)和极性(CPOL)分别可以为0或1,对应的4种组合构成了SPI的4种模式(mode) Mode 0:CPOL = 0,CPHA = 0 Mode 1:CPOL ...

  3. 很让人受教的提高php代码质量的方法

    1.不要使用相对路径 常常会看到: require_once('../../lib/some_class.php'); 该方法有很多缺点: 它首先查找指定的php包含路径, 然后查找当前目录. 因此会 ...

  4. C++中的友元函数的总结

    1.友元函数的简单介绍 1.1为什么要使用友元函数 在实现类之间数据共享时,减少系统开销,提高效率.如果类A中的函数要访问类B中的成员(例如:智能指针类的实现),那么类A中该函数要是类B的友元函数.具 ...

  5. C的动态链表建立

    运用到的函数为: 动态内存分配函数malloc()              比如:char *name=(char *)malloc(20);  相当与c++的new关键字 动态内存释放函数free ...

  6. stm32寄存器版学习笔记05 PWM

    STM32除TIM6和TIM7外都可以产生PWM输出.高级定时器TIM1和TIM8可以同时产生7路PWM,通用定时器可以产生4路PWM输出. 1.TIM1 CH1输出PWM配置步骤 ①开启TIM1时钟 ...

  7. Loj 2028 随机序列

    Loj 2028 随机序列 连续的乘号会将序列分成若干个块,块与块之间用加减号连接: \[ (a_1*a_2*...a_i)\pm(a_{i+1}*a_{i+2}*...a_j)\pm... \] 除 ...

  8. BZOJ5296 CQOI2018 破解D-H协议 【BSGS】

    BZOJ5296 CQOI2018Day1T1 破解D-H协议 Description Diffie-Hellman密钥交换协议是一种简单有效的密钥交换方法.它可以让通讯双方在没有事先约定密钥(密码) ...

  9. 在使用windows调用Hadoop 错误 /bin/bash: line 0: fg: no job control一般解决方法

    在使用windows调用Hadoop yarn平台的时候,一般都会遇到如下的错误: 2014-05-28 17:32:19,761 WARN org.apache.hadoop.yarn.server ...

  10. WINDOWS下kill进程的命令

    相信大家都有用命令行(CMD)解决问题的习惯,起码我感觉自己在处理Windows系统故障时越来越离不开Windows PE了,今天我想介绍两个很实用的命令:Tasklist与Tskill.命令:Tas ...