图片裁剪效果
准备在winform程序里做一个图片剪切功能,一个矩形框 有手柄 可进行调整 对图片进行裁剪。 就像ACDSee那样:
在园子里找了下没有现成的东东,准备自己整个。仔细想了下对图片进行裁剪 裁剪这个过程完全不用我们费心
因为.Net里盖茨大叔开的公司为我们提供了drawImage这个万能的函数啦,msdn里对他的大概解释是 把原图指定矩形区域的像素“画”到目标Image的指定矩形区域
来看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //图像裁剪 public Bitmap GetPartOfImage(Bitmap sourceImg, int width, int height, int offsetX, int offsetY) { Bitmap sourceBitmap = sourceImg; Bitmap resultBitmap = new Bitmap(width, height); using (Graphics g = Graphics.FromImage(resultBitmap)) { Rectangle resultRectangle = new Rectangle(0, 0, width, height); Rectangle sourceRectangle = new Rectangle(0 + offsetX, 0 + offsetY, width, height); //万能的drawImage函数,七七八八的参数非常多 普通的图片拉伸 放大 等效果都可以用它来做到。 //第一个参数:原图(被裁剪的图) //第二个参数:目标Image(裁剪后的图) //第三个参数:原图被裁剪的区域 //第四个参数:单位(当然是像素啦) g.DrawImage(sourceBitmap, resultRectangle, sourceRectangle, GraphicsUnit.Pixel); } return resultBitmap; } |
所以说裁剪的过程不需要费心 我们只需要实现那个矩形调整的效果就ok啦。
简而言之效果就是通过鼠标对一个矩形框进行:
在四个角的不同位置进行光标变换、 拖拽、在相应的位置固定某一个边 对矩形进行 上 下 左 右 等八个方向的拉伸。
好解释完了 懂否?调整好后把矩形框的 宽 高 坐标 传到上面的函数进行处理就ok啦。
好 核心的东西全解释完了 该出手了。
为了做得比较通用本人采用了继承自定义控件的方式,因为我是这么想的:
第一 继承了System.Windows.Forms.Control类后OnMouseOver 拖拽那些事件 都可以被天然的捕捉到然后交由自己定义的代码来处理
第二 你需要做的处理就是 通过不同的方式对控件的长宽进行调整 并复写Control类的OnPaint来进行实时的显示
第三 所有的东东都被封装到这个继承的控件里了 所有鼠标事件都与外界无关,这样以后使用更方便不用代码调来调去的 。
只需要在控件里公开一个获取选择区域的方法就可以了
首先是拖拽的效果,他的过程是这样的:在鼠标左键按下时记录当前控件相对于父容器的坐标 也就是this.location属性 还有鼠标的坐标,
鼠标移动时检查如果左键按下 则测算出当前鼠标位置跟原来记录的偏移量, 然后对原来记录的location进行偏移 赋值给this.location 直至鼠标弹起 完成一次拖拽的效果
鼠标左键重新按下并移动的时候重复上述过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | private Point m_MousePoint; private Point m_LastPoint; protected override void OnMouseDown(MouseEventArgs e) { base .OnMouseDown(e); this .m_LastPoint = this .Location; this .m_MousePoint = this .PointToScreen(e.Location); } protected override void OnMouseMove(MouseEventArgs e) { base .OnMouseMove(e); if (e.Button == MouseButtons.Left) { Point t = this .PointToScreen(e.Location); Point l = this .m_LastPoint; l.Offset(t.X - this .m_MousePoint.X, t.Y - this .m_MousePoint.Y); this .Location = l; } } |
矩形框上下左右拉伸不用多说了吧 原理嘛大家想想就明白了,onPaint时候矩形的绘制嘛自然是沿着控件的边框绘制就可以了噻
onMouseMove的时候矩形边框的判断嘛自然是根据控件的宽度跟高度加上location来的噻 ,在边框按下鼠标拖拽相应调整控件的宽高跟坐标就行了
好下面是该自定义控件的关键部分代码 包括矩形框手柄以及调整效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | protected override void OnMouseMove(MouseEventArgs e) { base .OnMouseMove(e); if (arow != arrowType.none) { reSize(e); return ; } if (e.Y <= recArrow[4].Bottom && e.Y >= recArrow[4].Top && e.X >= recArrow[4].Left && e.X <= recArrow[4].Right) //左上 Cursor = Cursors.SizeNWSE; else if (e.Y <= recArrow[5].Bottom && e.Y >= recArrow[5].Top && e.X >= recArrow[5].Left && e.X <= recArrow[5].Right) //左下 Cursor = Cursors.SizeNESW; else if (e.Y <= recArrow[6].Bottom && e.Y >= recArrow[6].Top && e.X >= recArrow[6].Left && e.X <= recArrow[6].Right) //右上 Cursor = Cursors.SizeNESW; else if (e.Y <= recArrow[7].Bottom && e.Y >= recArrow[7].Top && e.X >= recArrow[7].Left && e.X <= recArrow[7].Right) //右下 Cursor = Cursors.SizeNWSE; else if (e.Y <= recArrow[0].Bottom && e.Y >= recArrow[0].Top) //上 Cursor = Cursors.SizeNS; else if (e.Y <= recArrow[1].Bottom && e.Y >= recArrow[1].Top) //下 Cursor = Cursors.SizeNS; else if (e.X >= recArrow[2].Left && e.X <= recArrow[2].Right) //左 Cursor = Cursors.SizeWE; else if (e.X >= recArrow[3].Left && e.X <= recArrow[3].Right) //右 Cursor = Cursors.SizeWE; else Cursor = Cursors.SizeAll; if (e.Button == MouseButtons.Left) { Point t = this .PointToScreen(e.Location); Point l = this .m_LastPoint; if (e.Y <= recArrow[4].Bottom && e.Y >= recArrow[4].Top && e.X >= recArrow[4].Left && e.X <= recArrow[4].Right) //左上 arow = arrowType.leftUp; else if (e.Y <= recArrow[5].Bottom && e.Y >= recArrow[5].Top && e.X >= recArrow[5].Left && e.X <= recArrow[5].Right) //左下 arow = arrowType.leftDown; else if (e.Y <= recArrow[6].Bottom && e.Y >= recArrow[6].Top && e.X >= recArrow[6].Left && e.X <= recArrow[6].Right) //右上 arow = arrowType.rightUp; else if (e.Y <= recArrow[7].Bottom && e.Y >= recArrow[7].Top && e.X >= recArrow[7].Left && e.X <= recArrow[7].Right) //右下 arow = arrowType.rightDown; else if (e.Y <= recArrow[0].Bottom && e.Y >= recArrow[0].Top) //上 arow = arrowType.up; else if (e.Y <= recArrow[1].Bottom && e.Y >= recArrow[1].Top) //下 arow = arrowType.down; else if (e.X >= recArrow[2].Left && e.X <= recArrow[2].Right) //左 arow = arrowType.left; else if (e.X >= recArrow[3].Left && e.X <= recArrow[3].Right) //右 arow = arrowType.right; else arow = arrowType.none; l.Offset(t.X - this .m_MousePoint.X, t.Y - this .m_MousePoint.Y); if (arow != arrowType.none) reSize(e); else { this .Location = l; Refresh(); //这句很重要立即重绘 不然拖动到时候会出现卡卡 的现象 ,找了半天原因 } } } public void reSize(MouseEventArgs e) { Point t = this .PointToScreen(e.Location); Point l = this .m_LastPoint; l.Offset(t.X - this .m_MousePoint.X, t.Y - this .m_MousePoint.Y); switch (arow) { case arrowType.up: { this .Height = m_Size.Height - (t.Y - this .m_MousePoint.Y); this .Location = new Point(m_LastPoint.X, l.Y); break ; } case arrowType.down: { this .Height = m_Size.Height + (t.Y - this .m_MousePoint.Y); break ; } case arrowType.left: { this .Width = m_Size.Width - (t.X - this .m_MousePoint.X); this .Location = new Point(l.X, m_LastPoint.Y); break ; } case arrowType.right: { this .Width = m_Size.Width + (t.X - this .m_MousePoint.X); break ; } case arrowType.leftUp: { this .Width = m_Size.Width - (t.X - this .m_MousePoint.X); this .Height = m_Size.Height - (t.Y - this .m_MousePoint.Y); this .Location = new Point(l.X, l.Y); break ; } case arrowType.leftDown: { this .Width = m_Size.Width - (t.X - this .m_MousePoint.X); this .Height = m_Size.Height + (t.Y - this .m_MousePoint.Y); this .Location = new Point(l.X, m_LastPoint.Y); break ; } case arrowType.rightUp: { this .Width = m_Size.Width + (t.X - this .m_MousePoint.X); this .Height = m_Size.Height - (t.Y - this .m_MousePoint.Y); this .Location = new Point(m_LastPoint.X, l.Y); break ; } case arrowType.rightDown: { this .Width = m_Size.Width + (t.X - this .m_MousePoint.X); this .Height = m_Size.Height + (t.Y - this .m_MousePoint.Y); break ; } } this .Refresh(); } public enum arrowType { up,down,left,right,leftUp,leftDown,rightUp,rightDown,none } public arrowType arow=arrowType.none; Rectangle []recArrow= new Rectangle[8]; //8个手柄 public Rectangle area ; //选择区域 public readonly int blank=8; //边距 protected override void OnPaint(PaintEventArgs e) { int side = 6; //手柄矩形的边长 recArrow[0] = new Rectangle( new Point( this .Width / 2 - side / 2, blank - side / 2), new Size(side, side)); recArrow[1] = new Rectangle( new Point( this .Width / 2 - side / 2, this .Height - blank - side / 2), new Size(side, side)); recArrow[2] = new Rectangle( new Point(blank-side/2 , this .Height / 2 - side/2), new Size(side, side)); recArrow[3] = new Rectangle( new Point( this .Width-blank - side / 2, this .Height / 2 - side / 2), new Size(side, side)); recArrow[4] = new Rectangle( new Point(blank - side / 2, blank - side / 2), new Size(side, side)); recArrow[5] = new Rectangle( new Point(blank - side / 2, this .Height - blank - side / 2), new Size(side, side)); recArrow[6] = new Rectangle( new Point( this .Width - blank - side / 2, blank - side / 2), new Size(side, side)); recArrow[7] = new Rectangle( new Point( this .Width - blank - side / 2, this .Height - blank - side / 2), new Size(side, side)); foreach (Rectangle item in recArrow) e.Graphics.DrawRectangle(Pens.Red, item); area = new Rectangle( new Point(8, 8), new Size( this .Width-8*2, this .Height-8*2)); ////加上半透明效果看倒是好看了 重绘的时候卡的凶 ,期待有解决方法 e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; System.Drawing.Color cor = System.Drawing.Color.FromArgb(50, Color.Red); System.Drawing.SolidBrush bsh = new System.Drawing.SolidBrush(cor); e.Graphics.FillRectangle(bsh, area); e.Graphics.DrawRectangle(Pens.Red,area ); } |
最后还在一个老外的网站上扣了一段羽化效果的代码,可废了俺老大劲儿了。
好了大功告成走走看:
你是不是想说这图很給力哇(¯▽¯;)
这是测试程序(带源码)下载链接
可能有朋友说拖动的时候还一闪一闪的,俺只有这水平 尽力了哈 至少功能全部实现了吧,正在改进中。。。
要源码的朋友给俺发邮件哈,连代码跟图一起发给你 ,多半是冲着图来的吧 挖哈哈(¯▽¯;)。
重新编辑了 源码都发出来了哈,别留邮箱了 哥们儿些
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库