选择图像区域矩形框控件【原创】
1. 矩形框控件效果如何?
- 上下左右等8点可以拉伸
- 鼠标滑轮支持缩放,矩形框边框等比例缩放
- 选中矩形框左右拖拽
- 返回矩形框区域对应的图片的X,Y坐标
- 可同时支持多个矩形框
2. 矩形框使用方式?
- 初始化
//矩形框控件添加背景图片
rockRectControl.BackImage = bitmap;
//声明一个矩形框,传入左上角和右下角坐标
RockRectangle rect = new RockRectangle();
var p1 = item.DistinguishRegion.LeftTopCorner;
var p2 = item.DistinguishRegion.RightBottomCorner;
rect.Rectangle = Rectangle.FromLTRB((int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y);
//把矩形框添加到矩形框控件中,可以添加多个矩形
rockRectControl.RockRectangles.Add(rect);
- 获取矩形框区域对应的图片坐标
//找到矩形控件中某一个矩形框
Rectangle r = rockRectControl.RockRectangles[i].Rectangle;
//直接读取即可
var rp = new RockRegion();
rp.LeftTopCorner.X = r.X;
rp.LeftTopCorner.Y = r.Y;
rp.RightBottomCorner.X = r.Right;
rp.RightBottomCorner.Y = r.Bottom;
3. 矩形框控件源码?
- RockRectangle源码
using System.Drawing;
namespace NcModule.Tools;
[Serializable]
public class RockRectangle
{
private List littleRectangles = new List();
public Rectangle Rectangle { set; get; }
internal List GetLittleRectangles()
{
littleRectangles.Clear();
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.LeftUp));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.LeftMiddle));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.LeftBottom));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.BottomMiddle));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.RightUp));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.RightBottom));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.RightMiddle));
littleRectangles.Add(new LittleRectangle(Rectangle, PosSizableRect.UpMiddle));
return littleRectangles;
}
public double RotationAngle { set; get; }
}
internal class LittleRectangle
{
//小矩形的宽度
private int rectangleWidth = 8;
///
/// 矩形放大的倍数
///
public static double Enlarge = 1;
///
/// 小矩形的位置
///
public PosSizableRect Location { set; get; }
public Rectangle Rectangle { set; get; }
public LittleRectangle(Rectangle rect, PosSizableRect location)
{
this.Location = location;
switch (location)
{
case PosSizableRect.LeftUp:
this.Rectangle = createRectSizableNode(rect.X, rect.Y); break;
case PosSizableRect.LeftMiddle:
this.Rectangle = createRectSizableNode(rect.X, rect.Y + +rect.Height / 2); break;
case PosSizableRect.LeftBottom:
this.Rectangle = createRectSizableNode(rect.X, rect.Y + rect.Height); break;
case PosSizableRect.BottomMiddle:
this.Rectangle = createRectSizableNode(rect.X + rect.Width / 2, rect.Y + rect.Height); break;
case PosSizableRect.RightUp:
this.Rectangle = createRectSizableNode(rect.X + rect.Width, rect.Y); break;
case PosSizableRect.RightBottom:
this.Rectangle = createRectSizableNode(rect.X + rect.Width, rect.Y + rect.Height); break;
case PosSizableRect.RightMiddle:
this.Rectangle = createRectSizableNode(rect.X + rect.Width, rect.Y + rect.Height / 2); break;
case PosSizableRect.UpMiddle:
this.Rectangle = createRectSizableNode(rect.X + rect.Width / 2, rect.Y); break;
default:
this.Rectangle = new Rectangle(); break;
}
}
private Rectangle createRectSizableNode(int x, int y)
{
int rectWidth = (int)(rectangleWidth * Enlarge);
if (rectWidth
- RockRectControl源码
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace NcModule.Tools;
public partial class RockRectControl : UserControl
{
private Color borderColor = Color.Green;
private float borderWidth = 2;
private float defaultFontSize = 16;
private List rockRectangles = new List();
//是否显示序号
private bool isPrintNum = true;
private Font font = new Font("宋体", 16, FontStyle.Bold);
//背景图片
private Image backImage = default!;
//图片有效区域
private Rectangle effectiveRect = default(Rectangle);
//缩放比例,用double多次运算后会失真,故用百分比
private int zoomScale = 100;//图片本身的缩放比例
private int oldZoomScale = 100;
private int zoomMinScale = 60;
private int zoomMaxScale = 500;
private int stepScale = 20;//每次缩放比例
private Bitmap cloneBackImage = default!;
private double imageScale;//真实图片与显示是的缩放比例
private Point realImageCorePoint = new Point();//真实图片的中心点坐标偏移量,当放大或拖拽 时中心点发生变更
private Point wheelPoint = new Point();//滚动时的坐标
private bool zoomScaleIsUpdate = true;
public Image BackImage
{
set
{
this.backImage = value;
if (this.backImage != null)
{
//黑白图,故格式用Format16bppRgb555,可以降低内存
cloneBackImage = new Bitmap(this.backImage.Width, this.backImage.Height, PixelFormat.Format16bppRgb555);
}
}
get { return this.backImage; }
}
///
/// 矩形框的颜色
///
public Color BorderColor
{
set { this.borderColor = value; }
get { return this.borderColor; }
}
///
/// 矩形框边框的粗细
///
public float BorderWidth
{
set { this.borderWidth = value; }
get { return this.borderWidth; }
}
public List RockRectangles
{
get { return this.rockRectangles; }
}
public RockRectControl()
{
InitializeComponent();
this.init();
}
private void init()
{
//双缓冲
this.DoubleBuffered = true;
}
private void setFitImageRect()
{
if (this.cloneBackImage == null)
{
return;
}
double imageAspect = this.cloneBackImage.Width * 1.0 / this.cloneBackImage.Height;
double controlAspect = this.Width * 1.0 / this.Height;
//以高为主
if (imageAspect 0)//上滚放大
{
if (this.zoomScale this.zoomMinScale)
{
this.stepScale = -Math.Abs(this.stepScale);
this.zoomScale += this.stepScale;
}
}
this.Invalidate();
}
protected override void OnSizeChanged(EventArgs e)
{
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs pe)
{
//背景图片存在才绘制
if (this.cloneBackImage != null)
{
this.setFitImageRect();
//把矩形画在背景图片上
this.paintRect();
//把图片绘制到界面上
this.paintImageToControl(pe.Graphics);
}
}
//在背景图片上画框
private void paintRect()
{
var g = Graphics.FromImage(cloneBackImage);
//画背景图
g.DrawImage(this.backImage, 0, 0, cloneBackImage.Width, cloneBackImage.Height);
//画的线平滑
//g.InterpolationMode = InterpolationMode.Low;
//设置高质量,低速度呈现平滑程度
//g.SmoothingMode = SmoothingMode.HighSpeed;
g.CompositingQuality = CompositingQuality.AssumeLinear;
//在图像上矩形
using (var path = new GraphicsPath())
{
foreach (var item in this.RockRectangles)
{
//动态加粗线条
double enlarge = this.imageScale * 100 / this.zoomScale;
float nBorderWidth = (float)(this.borderWidth * enlarge);
if (nBorderWidth
/// 判断某个点是否在矩形内
///
///
///
///
///
///
private bool isInRect(Point p, Rectangle rect, double angle, Point centerP)
{
//获取反旋转后的点
Point rotateP = this.getRotatePoint(p, -angle, centerP);
return rect.Contains(rotateP);
}
///
/// 改变鼠标的图标
///
///
private void changeCursor(Point p, bool updateSelectData = false)
{
bool isInBigRect = false;
foreach (var item in this.RockRectangles)
{
if (this.isInRect(p, item.Rectangle, item.RotationAngle))
{
isInBigRect = true;
}
foreach (var littleRect in item.GetLittleRectangles())
{
//如果图标在小矩形内
if (this.isInRect(p, littleRect.Rectangle, item.RotationAngle, this.getCenter(item.Rectangle)))
{
this.Cursor = this.getCursor(littleRect.Location);
if (updateSelectData)
{
this.selectRectIndex = this.RockRectangles.IndexOf(item);
this.selectLocation = littleRect.Location;
}
return;
}
}
}
if (isInBigRect)
{
this.Cursor = Cursors.SizeAll;
}
else
{
this.Cursor = Cursors.Default;
}
}
private Cursor getCursor(PosSizableRect p)
{
switch (p)
{
case PosSizableRect.LeftUp:
return Cursors.SizeNWSE;
case PosSizableRect.LeftMiddle:
return Cursors.SizeWE;
case PosSizableRect.LeftBottom:
return Cursors.SizeNESW;
case PosSizableRect.BottomMiddle:
return Cursors.SizeNS;
case PosSizableRect.RightUp:
return Cursors.SizeNESW;
case PosSizableRect.RightBottom:
return Cursors.SizeNWSE;
case PosSizableRect.RightMiddle:
return Cursors.SizeWE;
case PosSizableRect.UpMiddle:
return Cursors.SizeNS;
default:
return Cursors.Default;
}
}
//获取矩形中心
private Point getCenter(Rectangle rect)
{
return new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);
}
///
/// 获取矩形旋转后的路径
///
///
///
private void getPath(GraphicsPath path, Rectangle rect, double angle)
{
Point center = this.getCenter(rect);
this.getPath(path, rect, angle, center);
}
private void getPath(GraphicsPath path, Rectangle rect, double angle, Point center)
{
path.AddRectangle(rect);
var a = -angle * (Math.PI / 180);
var n1 = (float)Math.Cos(a);
var n2 = (float)Math.Sin(a);
var n3 = -(float)Math.Sin(a);
var n4 = (float)Math.Cos(a);
var n5 = (float)(center.X * (1 - Math.Cos(a)) + center.Y * Math.Sin(a));
var n6 = (float)(center.Y * (1 - Math.Cos(a)) - center.X * Math.Sin(a));
Matrix matrix = new Matrix(n1, n2, n3, n4, n5, n6);
path.Transform(matrix);
}
//p1绕center旋转angle角度后点位
private Point getRotatePoint(Point p1, double angle, Point center)
{
//使用旋转矩阵求值
System.Windows.Media.RotateTransform rotateTransform = new System.Windows.Media.RotateTransform(angle, center.X, center.Y);
System.Windows.Point p = new System.Windows.Point(p1.X, p1.Y);
System.Windows.Point p2 = rotateTransform.Transform(p);
Point result = new Point();
result.X = (int)p2.X;
result.Y = (int)p2.Y;
return result;
}
}
4. 矩形框控件不足?
- 目前矩形框控件不支持对背景图片的拖拽(本项目中未涉及此场景,后续可能会增加此功能)
- 目前矩形框控件不支持旋转(源码中有旋转矩形框展示代码,但交互上没有实现,需要人为赋值旋转角度,后续可能会优化)
作者:Bonker 出处:http://www.cnblogs.com/Bonker QQ:519841366 |
本页版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,
且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利
文章来源于互联网:选择图像区域矩形框控件【原创】
THE END
二维码