diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Properties/AssemblyInfo.cs b/Windows/CS/Framework4.0/Toprie/Toprie/Properties/AssemblyInfo.cs index 4a10be8..51de4bc 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/Properties/AssemblyInfo.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/Properties/AssemblyInfo.cs @@ -33,6 +33,6 @@ using System.Runtime.InteropServices; //通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.2.5")] -[assembly: AssemblyFileVersion("1.0.2.7")] +[assembly: AssemblyFileVersion("1.0.2.8")] // NuGet包相关信息已在项目文件中配置 diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/preview.cs b/Windows/CS/Framework4.0/Toprie/Toprie/preview.cs index 84fc221..5cfd760 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/preview.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/preview.cs @@ -14,12 +14,27 @@ namespace JoyD.Windows.CS /// public partial class preview : Form { + // 原始温度数据矩阵尺寸 + private const int DATA_WIDTH = 256; + private const int DATA_HEIGHT = 192; + // 热力图显示尺寸 + private const int DISPLAY_WIDTH = 512; + private const int DISPLAY_HEIGHT = 384; + + // 鼠标当前指向的数据点坐标 + private Point _currentDataPoint = new Point(-1, -1); + // 用于保护_currentDataPoint访问的锁对象 + private readonly object _dataPointLock = new object(); + /// /// 初始化预览窗口的构造函数 /// public preview() { InitializeComponent(); + previewImageBox.MouseMove += PreviewImageBox_MouseMove; + previewImageBox.MouseLeave += PreviewImageBox_MouseLeave; + previewImageBox.Paint += PreviewImageBox_Paint; } /// @@ -48,6 +63,9 @@ namespace JoyD.Windows.CS { previewImageBox.Image = (Image)image.Clone(); } + + // 图像更新后,确保重绘以保持覆盖层正确 + previewImageBox.Invalidate(); } catch (Exception ex) { @@ -62,6 +80,11 @@ namespace JoyD.Windows.CS { base.OnFormClosed(e); + // 移除事件绑定 + previewImageBox.MouseMove -= PreviewImageBox_MouseMove; + previewImageBox.MouseLeave -= PreviewImageBox_MouseLeave; + previewImageBox.Paint -= PreviewImageBox_Paint; + // 释放图像资源 if (previewImageBox.Image != null) { @@ -69,5 +92,126 @@ namespace JoyD.Windows.CS previewImageBox.Image = null; } } + + // 鼠标移动事件处理 + private void PreviewImageBox_MouseMove(object sender, MouseEventArgs e) + { + if (previewImageBox.Image == null) return; + + // 计算鼠标在图像上的坐标(考虑图像缩放) + Point imagePoint = GetImagePoint(e.Location); + + // 映射到原始数据点坐标 + Point dataPoint = MapToDataPoint(imagePoint); + + // 使用锁保护_currentDataPoint的访问 + lock (_dataPointLock) + { + // 如果数据点发生变化,重绘控件 + if (_currentDataPoint != dataPoint) + { + _currentDataPoint = dataPoint; + previewImageBox.Invalidate(); // 触发Paint事件重绘 + } + } + } + + // 鼠标离开事件处理 + private void PreviewImageBox_MouseLeave(object sender, EventArgs e) + { + // 使用锁保护_currentDataPoint的访问 + lock (_dataPointLock) + { + _currentDataPoint = new Point(-1, -1); + previewImageBox.Invalidate(); // 触发Paint事件重绘 + } + } + + // 将控件坐标转换为图像坐标 + private Point GetImagePoint(Point controlPoint) + { + if (previewImageBox.Image == null) + return controlPoint; + + // 计算图像在控件中的缩放比例 + // SizeMode = StretchImage 将图像拉伸填满整个PictureBox + // 控件左上角对应图像左上角,控件右下角对应图像右下角 + float scaleX = (float)previewImageBox.Image.Width / previewImageBox.ClientSize.Width; + float scaleY = (float)previewImageBox.Image.Height / previewImageBox.ClientSize.Height; + + // 计算图像坐标 + return new Point( + (int)(controlPoint.X * scaleX), + (int)(controlPoint.Y * scaleY) + ); + } + + // 将图像坐标映射到原始数据点坐标 + private Point MapToDataPoint(Point imagePoint) + { + // 确保坐标在有效范围内 + int x = Math.Max(0, Math.Min(imagePoint.X, DISPLAY_WIDTH - 1)); + int y = Math.Max(0, Math.Min(imagePoint.Y, DISPLAY_HEIGHT - 1)); + + // 计算原始数据点坐标(整数除法,向下取整) + return new Point(x / 2, y / 2); + } + + // 将原始数据点坐标映射到热力图覆盖区域 + private Rectangle GetDataPointCoverageArea(Point dataPoint) + { + // 确保数据点在有效范围内 + int x = Math.Max(0, Math.Min(dataPoint.X, DATA_WIDTH - 1)); + int y = Math.Max(0, Math.Min(dataPoint.Y, DATA_HEIGHT - 1)); + + // 计算覆盖区域(2×2像素) + return new Rectangle(x * 2, y * 2, 2, 2); + } + + // Paint事件处理函数 + private void PreviewImageBox_Paint(object sender, PaintEventArgs e) + { + // 边界检查:如果没有图像,不需要绘制覆盖层 + if (previewImageBox.Image == null) + return; + + // 使用锁保护_currentDataPoint的访问并进行有效性检查 + Point currentDataPoint; + lock (_dataPointLock) + { + currentDataPoint = _currentDataPoint; + // 如果没有有效的数据点,不需要绘制覆盖层 + if (currentDataPoint.X < 0 || currentDataPoint.Y < 0) + return; + } + + // 计算数据点在图像坐标系中的覆盖区域 + Rectangle imageArea = GetDataPointCoverageArea(currentDataPoint); + + // 转换为控件坐标系 + float scaleX = (float)previewImageBox.ClientSize.Width / previewImageBox.Image.Width; + float scaleY = (float)previewImageBox.ClientSize.Height / previewImageBox.Image.Height; + + Rectangle controlArea = new Rectangle( + (int)(imageArea.X * scaleX), + (int)(imageArea.Y * scaleY), + (int)(imageArea.Width * scaleX), + (int)(imageArea.Height * scaleY) + ); + + // 使用using语句确保GDI+资源正确释放 + using (Brush semiTransparentRed = new SolidBrush(Color.FromArgb(128, Color.Red))) + { + // 绘制覆盖层 + e.Graphics.FillRectangle(semiTransparentRed, controlArea); + + // 使用using语句确保Pen资源正确释放 + using (Pen borderPen = new Pen(Color.Red, 1)) + { + // 可选:绘制矩形边框以增强视觉效果 + e.Graphics.DrawRectangle(borderPen, controlArea); + } + } + } } }