您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

C# GDI+ 实现橡皮筋技术

2024/5/17 19:35:24发布28次查看
应该有很多人都在寻找这方面的资料,看看下面我做的,或许对你会有所帮助,但愿如此。
为了实现橡皮筋技术,我用了两种方法:
    第一种是利用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,但要注意的是在左键按下和鼠标拖动的情况下不能使用抗锯齿技术。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product