diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/README.md b/Windows/CS/Framework4.0/Toprie/Toprie/README.md index a6fdd51..de0a751 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/README.md +++ b/Windows/CS/Framework4.0/Toprie/Toprie/README.md @@ -146,4 +146,5 @@ #### 区域绘制逻辑 1. 创建独立的叠加层图像 :专门维护一个与显示图像同尺寸的Image对象作为矩形框的叠加层 2. 分离绘制逻辑 :将临时绘制和最终绘制分离,临时矩形仍通过Paint事件显示,完成的矩形绘制到叠加层 -3. 图像合并机制 :在Paint事件中先绘制叠加层,再绘制临时矩形。 \ No newline at end of file +3. 图像合并机制 :在Paint事件中先绘制叠加层,再绘制临时矩形。 +4. 保存的矩形框位置和大小信息应该是相对于图像的,而不是相对于控件的。 \ No newline at end of file diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Setting.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Setting.cs index c0b8c81..8db1326 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Setting.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Setting.cs @@ -36,6 +36,9 @@ namespace JoyD.Windows.CS { InitializeComponent(); + // 订阅SizeChanged事件,确保控件大小变化时矩形框正确缩放 + this.picBoxTemp.SizeChanged += new EventHandler(PicBoxTemp_SizeChanged); + // 设置按钮图标 SetButtonIcon(); @@ -82,6 +85,65 @@ namespace JoyD.Windows.CS } } + /// + /// 将控件坐标转换为图像坐标 + /// + private Point ControlPointToImagePoint(Point controlPoint) + { + if (picBoxTemp.Image == null) + return controlPoint; + + // 计算图像在控件中的缩放比例 + float scaleX = (float)picBoxTemp.Image.Width / picBoxTemp.ClientSize.Width; + float scaleY = (float)picBoxTemp.Image.Height / picBoxTemp.ClientSize.Height; + + // 计算图像坐标 + return new Point( + (int)(controlPoint.X * scaleX), + (int)(controlPoint.Y * scaleY) + ); + } + + /// + /// 将图像坐标转换为控件坐标 + /// + private Point ImagePointToControlPoint(Point imagePoint) + { + if (picBoxTemp.Image == null) + return imagePoint; + + // 计算图像在控件中的缩放比例 + float scaleX = (float)picBoxTemp.ClientSize.Width / picBoxTemp.Image.Width; + float scaleY = (float)picBoxTemp.ClientSize.Height / picBoxTemp.Image.Height; + + // 计算控件坐标 + return new Point( + (int)(imagePoint.X * scaleX), + (int)(imagePoint.Y * scaleY) + ); + } + + /// + /// 将图像矩形转换为控件矩形 + /// + private Rectangle ImageRectangleToControlRectangle(Rectangle imageRectangle) + { + if (picBoxTemp.Image == null) + return imageRectangle; + + // 计算图像在控件中的缩放比例 + float scaleX = (float)picBoxTemp.ClientSize.Width / picBoxTemp.Image.Width; + float scaleY = (float)picBoxTemp.ClientSize.Height / picBoxTemp.Image.Height; + + // 计算控件矩形 + return new Rectangle( + (int)(imageRectangle.X * scaleX), + (int)(imageRectangle.Y * scaleY), + (int)(imageRectangle.Width * scaleX), + (int)(imageRectangle.Height * scaleY) + ); + } + /// /// 鼠标按下事件 - 开始绘制矩形 /// @@ -116,11 +178,12 @@ namespace JoyD.Windows.CS } /// - /// 存储区域信息的类,包含矩形框、颜色和序号 + /// 存储区域信息的类,包含矩形框(图像坐标)、颜色和序号 /// private class RegionInfo { - public Rectangle Rectangle { get; set; } + // 存储相对于图像的矩形坐标 + public Rectangle ImageRectangle { get; set; } public Color Color { get; set; } public int Index { get; set; } } @@ -128,6 +191,7 @@ namespace JoyD.Windows.CS /// /// 创建或更新叠加层图像(完全重绘) /// 仅在必要时调用,如图像尺寸改变或需要完全重绘 + /// 使用图像坐标绘制矩形 /// private void CreateRectangleOverlayImage() { @@ -182,6 +246,7 @@ namespace JoyD.Windows.CS /// /// 将单个区域绘制到叠加层图像 /// 用于增量绘制,避免每次都重绘所有矩形 + /// 使用图像坐标绘制矩形 /// /// 要绘制的区域信息 private void DrawRegionToOverlay(RegionInfo region) @@ -191,16 +256,19 @@ namespace JoyD.Windows.CS using (Graphics g = Graphics.FromImage(_rectangleOverlayImage)) { + // 设置高质量绘图 + g.SmoothingMode = SmoothingMode.AntiAlias; + // 使用每个区域自己的颜色绘制矩形 - g.DrawRectangle(new Pen(region.Color, 2), region.Rectangle); + g.DrawRectangle(new Pen(region.Color, 2), region.ImageRectangle); // 绘制区域序号 using (Font font = new Font("Arial", 12, FontStyle.Bold)) using (SolidBrush brush = new SolidBrush(region.Color)) { // 在矩形左上角绘制序号 - Point textPosition = new Point(region.Rectangle.X + 5, region.Rectangle.Y - 15); - // 确保文本不超出控件边界 + Point textPosition = new Point(region.ImageRectangle.X + 5, region.ImageRectangle.Y - 15); + // 确保文本不超出图像边界 if (textPosition.Y < 0) textPosition.Y = 5; @@ -212,21 +280,33 @@ namespace JoyD.Windows.CS /// /// 鼠标释放事件 - 完成矩形绘制(优化版) /// 使用增量绘制,避免每次都重绘所有矩形 + /// 确保矩形坐标相对于图像而非控件 /// private void PicBoxTemp_MouseUp(object sender, MouseEventArgs e) { - if (_isDrawing && _isDrawingMode && e.Button == MouseButtons.Left) + if (_isDrawing && _isDrawingMode && e.Button == MouseButtons.Left && picBoxTemp.Image != null) { _isDrawing = false; - // 确保矩形有一定大小才添加 - if (_currentRectangle.Width > 10 && _currentRectangle.Height > 10) + // 获取相对于图像的矩形坐标 + Point imageStartPoint = ControlPointToImagePoint(new Point(_currentRectangle.X, _currentRectangle.Y)); + Point imageEndPoint = ControlPointToImagePoint(new Point(_currentRectangle.Right, _currentRectangle.Bottom)); + + // 确保矩形有一定大小才添加(转换为图像坐标后检查) + int imageWidth = Math.Abs(imageEndPoint.X - imageStartPoint.X); + int imageHeight = Math.Abs(imageEndPoint.Y - imageStartPoint.Y); + + if (imageWidth > 5 && imageHeight > 5) { - // 增加计数器并创建新的区域信息 + // 计算图像坐标的矩形(确保左上角为起始点) + int imageX = Math.Min(imageStartPoint.X, imageEndPoint.X); + int imageY = Math.Min(imageStartPoint.Y, imageEndPoint.Y); + + // 增加计数器并创建新的区域信息(存储图像坐标) _regionCounter++; RegionInfo regionInfo = new RegionInfo { - Rectangle = _currentRectangle, + ImageRectangle = new Rectangle(imageX, imageY, imageWidth, imageHeight), Color = _selectedColor, Index = _regionCounter }; @@ -238,9 +318,8 @@ namespace JoyD.Windows.CS // 如果叠加层不存在或尺寸不匹配,需要完全重建 if (_rectangleOverlayImage == null || - (picBoxTemp.Image != null && ( - _rectangleOverlayImage.Width != picBoxTemp.Image.Width || - _rectangleOverlayImage.Height != picBoxTemp.Image.Height))) + _rectangleOverlayImage.Width != picBoxTemp.Image.Width || + _rectangleOverlayImage.Height != picBoxTemp.Image.Height) { needFullRebuild = true; } @@ -259,9 +338,9 @@ namespace JoyD.Windows.CS // 显示绘制完成的提示 ToolStripStatusLabel statusLabel = new ToolStripStatusLabel { - Text = string.Format("已添加检测区域{0}: X={1}, Y={2}, 宽={3}, 高={4}", + Text = string.Format("已添加检测区域{0}: 图像坐标 - X={1}, Y={2}, 宽={3}, 高={4}", _regionCounter, - _currentRectangle.X, _currentRectangle.Y, _currentRectangle.Width, _currentRectangle.Height) + imageX, imageY, imageWidth, imageHeight) }; // 如果有状态栏,可以添加到状态栏显示 } @@ -273,17 +352,25 @@ namespace JoyD.Windows.CS /// /// 绘制事件 - 显示矩形框(实现图像合并机制) + /// 处理叠加层图像的缩放,确保与控件尺寸匹配 /// private void PicBoxTemp_Paint(object sender, PaintEventArgs e) { - // 图像合并机制:先绘制叠加层 - if (_rectangleOverlayImage != null) + // 设置高质量绘图 + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + + // 图像合并机制:先绘制叠加层(根据控件尺寸进行缩放) + if (_rectangleOverlayImage != null && picBoxTemp.Image != null) { - e.Graphics.DrawImage(_rectangleOverlayImage, Point.Empty); + // 计算缩放后的目标矩形 + Rectangle destRect = new Rectangle(0, 0, picBoxTemp.ClientSize.Width, picBoxTemp.ClientSize.Height); + // 绘制缩放后的叠加层 + e.Graphics.DrawImage(_rectangleOverlayImage, destRect, 0, 0, _rectangleOverlayImage.Width, _rectangleOverlayImage.Height, GraphicsUnit.Pixel); } - // 再绘制临时矩形(当前正在绘制的矩形) - if (!_currentRectangle.IsEmpty) + // 再绘制临时矩形(当前正在绘制的矩形,使用控件坐标) + if (!_currentRectangle.IsEmpty && _isDrawingMode && _isDrawing) { using (Pen dashedPen = new Pen(Color.Blue, 2)) { @@ -434,6 +521,27 @@ namespace JoyD.Windows.CS /// /// 定时器每秒触发的事件处理方法 /// + /// + /// 当图像更新或控件大小变化时,重新创建叠加层图像 + /// 确保矩形框正确显示在新的尺寸下 + /// + private void UpdateOverlayForSizeChange() + { + if (picBoxTemp.Image != null && _drawnRectangles.Count > 0) + { + CreateRectangleOverlayImage(); + picBoxTemp.Invalidate(); + } + } + + /// + /// 当控件大小改变时,更新叠加层以确保矩形框正确缩放 + /// + private void PicBoxTemp_SizeChanged(object sender, EventArgs e) + { + UpdateOverlayForSizeChange(); + } + private void Timer_Tick(object sender, EventArgs e) { // 这里可以添加每秒需要执行的代码