实现新的图像更新逻辑:添加LastImage和InfoImage处理

This commit is contained in:
zqm
2025-10-30 09:56:02 +08:00
parent fa98cbec65
commit 113fef2094

View File

@@ -19,7 +19,25 @@ namespace JoyD.Windows.CS.Toprie
// 是否正在接收图像
private bool _isReceivingImage = false;
// 全局图像缓冲区bitmap用于在其上绘制图像和mask信息
private Bitmap _imageBuffer = null;
private const int BUFFER_WIDTH = 512;
private const int BUFFER_HEIGHT = 384;
// 最后接收的图像
private Image _lastImage = null;
// 信息图像,用于显示额外信息
private Image _infoImage = null;
// 用于保护_lastImage的线程锁
private readonly object _lastImageLock = new object();
// 用于保护_infoImage的线程锁
private readonly object _infoImageLock = new object();
// 是否显示信息图像
private bool _isDisplayingInfo = false;
// 项目路径,用于数据文件的存取
private string _projectPath = "";
@@ -43,10 +61,10 @@ namespace JoyD.Windows.CS.Toprie
if (_deviceManager != null)
{
_deviceManager.ProjectPath = _projectPath;
}
}
}
}
}
}
}
// 显示错误的定时器
private System.Windows.Forms.Timer _errorDisplayTimer;
@@ -93,8 +111,8 @@ namespace JoyD.Windows.CS.Toprie
{
Console.WriteLine($"处理暂停/恢复图像更新时出错: {ex.Message}");
}
}
}
public Camera()
{
InitializeComponent();
@@ -105,6 +123,9 @@ namespace JoyD.Windows.CS.Toprie
// 将设计模式状态传递给DeviceManager
UpdateDesignModeStatus();
// 初始化图像缓冲区
InitializeImageBuffer();
// 只有在非设计模式下才初始化设备管理器和错误定时器
if (!DesignMode)
{
@@ -180,7 +201,32 @@ namespace JoyD.Windows.CS.Toprie
_deviceManager.ConnectionException += DeviceManager_ConnectionException;
}
}
/// <summary>
/// 初始化图像缓冲区
/// </summary>
private void InitializeImageBuffer()
{
try
{
// 创建512*384大小的bitmap作为图像缓冲区
_imageBuffer = new Bitmap(BUFFER_WIDTH, BUFFER_HEIGHT);
Console.WriteLine($"图像缓冲区已初始化: {BUFFER_WIDTH}x{BUFFER_HEIGHT}");
// 初始化缓冲区为黑色背景
using (Graphics g = Graphics.FromImage(_imageBuffer))
{
g.Clear(Color.Black);
}
}
catch (Exception ex)
{
Console.WriteLine($"初始化图像缓冲区失败: {ex.Message}");
// 如果初始化失败确保_imageBuffer为null
_imageBuffer = null;
}
}
/// <summary>
/// 初始化错误显示定时器
/// </summary>
@@ -329,7 +375,7 @@ namespace JoyD.Windows.CS.Toprie
if (pauseImageUpdateToolStripMenuItem.Text == "恢复图像更新")
return;
if (DesignMode) return;
Image image = null;
try
{
if (e.ImageData != null && e.ImageData.Length > 0)
@@ -344,306 +390,253 @@ namespace JoyD.Windows.CS.Toprie
using (Image tempImage = System.Drawing.Image.FromStream(ms))
{
// 创建一个全新的位图而不仅仅是克隆,确保数据完整性
image = new Bitmap(tempImage);
Image newImage = new Bitmap(tempImage);
// 立即验证新创建的图像是否有效
try
{
// 访问Width和Height属性来验证图像是否有效
int width = image.Width;
int height = image.Height;
int width = newImage.Width;
int height = newImage.Height;
if (width <= 0 || height <= 0)
{
Console.WriteLine("创建的图像尺寸无效");
image.Dispose();
image = null;
newImage.Dispose();
return;
}
}
catch (Exception)
{
Console.WriteLine("创建的图像无效");
image.Dispose();
image = null;
newImage.Dispose();
return;
}
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"创建图像失败: {ex.Message}");
// 确保在异常情况下释放资源
if (image != null)
{
image.Dispose();
image = null;
}
}
if (image == null)
{
Console.WriteLine("接收到空或无效的图像数据");
return;
}
try
{
// 尝试创建图像的克隆以传递给UI线程
Image clonedImage = null;
try
{
clonedImage = (Image)image.Clone();
// 验证克隆的图像是否有效
if (clonedImage.Width <= 0 || clonedImage.Height <= 0)
{
Console.WriteLine("克隆的图像尺寸无效");
clonedImage.Dispose();
image.Dispose(); // 确保原始图像也被释放
return;
}
}
catch (Exception ex)
{
Console.WriteLine($"克隆图像失败: {ex.Message}");
image.Dispose(); // 确保原始图像被释放
return;
}
// 确保在UI线程上更新并且检查控件是否已被释放
if (!this.IsDisposed && !this.Disposing)
{
if (this.InvokeRequired)
{
try
{
// 创建本地副本以避免闭包问题
Image imageForUI = clonedImage;
clonedImage = null; // 防止在异步操作期间被修改
// 使用BeginInvoke在UI线程上更新图像避免可能的死锁问题
this.BeginInvoke(new Action(() =>
{
try
// 按照用户要求收到图像数据后将图像保存到LastImage中调用更新
lock (_lastImageLock)
{
UpdateImageOnUI(imageForUI);
}
catch (Exception ex)
{
Console.WriteLine($"更新UI图像失败: {ex.Message}");
// 如果UI更新失败确保克隆的图像被释放
if (imageForUI != null)
// 释放旧的LastImage资源
if (_lastImage != null)
{
try
{
imageForUI.Dispose();
}
catch {}
try { _lastImage.Dispose(); } catch {}
}
// 设置新的LastImage
_lastImage = newImage;
}
}));
}
catch (Exception)
{
// 异常情况下确保释放图像资源
if (clonedImage != null)
{
try
// 调用更新UI
this.BeginInvoke(new Action(() =>
{
clonedImage.Dispose();
}
catch {}
clonedImage = null;
try
{
UpdateImageOnUI();
}
catch (Exception ex)
{
Console.WriteLine($"更新UI图像失败: {ex.Message}");
}
}));
}
throw;
}
}
else
{
// 在UI线程上直接更新图像
UpdateImageOnUI(clonedImage);
}
}
else
{
// 如果控件已释放,确保释放图像资源
clonedImage.Dispose();
}
// 释放原始图像资源
image.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"处理图像接收事件失败: {ex.Message}");
// 确保在任何异常情况下都释放原始图像
if (image != null)
{
try
{
image.Dispose();
}
catch {}
}
Console.WriteLine($"处理接收到的图像时出错: {ex.Message}");
}
}
/// <summary>
/// 在UI线程上更新图像
/// </summary>
private void UpdateImageOnUI(Image image)
{
private void UpdateImageOnUI(Image image) // 保留原有方法签名,处理旧调用
{
UpdateImageOnUI();
}
/// <summary>
/// 在UI线程上更新图像 - 新实现,按照用户要求:
/// 1. 先将LastImage绘制到全局缓冲
/// 2. 再将InfoImage绘制到缓冲
/// 3. 最后一次性绘制到图像框的bitmap
/// </summary>
private void UpdateImageOnUI()
{
if (DesignMode) return;
// 线程安全检查 - 确保在UI线程上执行
if (this.InvokeRequired)
{
{
try
{
this.BeginInvoke(new Action<Image>(UpdateImageOnUI), image);
{
this.BeginInvoke(new Action(UpdateImageOnUI));
}
catch (ObjectDisposedException)
{
// 如果控件已被释放,确保释放图像资源
if (image != null)
{
try { image.Dispose(); } catch {}
}
{
Console.WriteLine("控件已释放跳过UI更新");
}
return;
}
// 空值检查
if (image == null)
{
Console.WriteLine("传入UpdateImageOnUI的图像为空");
return;
}
// 连接状态检查 - 只在设备实际连接时处理图像
if (_deviceManager.ConnectionStatus != ConnectionStatus.Connected)
{
Console.WriteLine("设备未连接,跳过图像更新");
try { image.Dispose(); } catch {}
return;
}
// 增强图像有效性检查
bool isImageValid = false;
Image safeImage = null;
Image lastImage = null;
Image infoImage = null;
Image oldImage = null;
try
{
// 创建一个安全的图像副本,确保原图像不被破坏
safeImage = new Bitmap(image);
{
// 连接状态检查 - 只在设备实际连接时处理图像
if (_deviceManager.ConnectionStatus != ConnectionStatus.Connected)
{
Console.WriteLine("设备未连接,跳过图像更新");
return;
}
// 预先验证图像是否有效
if (safeImage.Width > 0 && safeImage.Height > 0)
{
// 尝试访问图像的像素数据,这是更严格的验证方式
using (Graphics g = Graphics.FromImage(new Bitmap(1, 1)))
{
// 简单绘制操作验证图像是否可绘制
g.DrawImage(safeImage, 0, 0, 1, 1);
// 检查图像缓冲区是否有效
if (_imageBuffer == null)
{
Console.WriteLine("图像缓冲区未初始化,尝试重新初始化");
InitializeImageBuffer();
if (_imageBuffer == null)
{
Console.WriteLine("重新初始化图像缓冲区失败");
return;
}
isImageValid = true;
Console.WriteLine($"图像验证成功: {safeImage.Width}x{safeImage.Height}");
}
else
{
Console.WriteLine($"图像尺寸无效: {safeImage.Width}x{safeImage.Height}");
}
}
catch (Exception ex)
{
Console.WriteLine($"图像验证失败: {ex.Message}");
// 释放临时创建的安全图像
if (safeImage != null)
{
try { safeImage.Dispose(); } catch {}
}
// 释放原始图像
try { image.Dispose(); } catch {}
return;
}
try
{
// 更新图像前先检查控件是否存在/已释放
if (this.IsDisposed || imageBox == null || imageBox.IsDisposed)
{
// 如果控件已释放,确保图像也被释放
try { safeImage.Dispose(); } catch {}
try { image.Dispose(); } catch {}
{
Console.WriteLine("控件已释放,无法更新图像");
return;
}
// 保存旧图像引用,以便在设置新图像后释放
Image oldImage = imageBox.Image;
oldImage = imageBox.Image;
try
{
// 确保控件仍处于有效状态
if (imageBox.IsDisposed || !isImageValid || safeImage == null)
{
try { safeImage.Dispose(); } catch {}
try { image.Dispose(); } catch {}
return;
}
// 尝试设置新图像(使用验证过的安全副本)
imageBox.Image = safeImage;
// 验证图像是否成功设置
if (imageBox.Image != safeImage)
{
Console.WriteLine("图像设置失败,可能参数无效");
try { safeImage.Dispose(); } catch {}
try { image.Dispose(); } catch {}
return;
}
// 释放原始图像,因为我们现在使用的是安全副本
try { image.Dispose(); } catch {}
// 仅在新图像成功设置后释放旧图像
if (oldImage != null && oldImage != safeImage) // 防止自引用释放
{
try
{
oldImage.Dispose();
}
catch {}
// 获取当前的LastImage引用
lock (_lastImageLock)
{
if (_lastImage != null)
{
lastImage = (Image)_lastImage.Clone();
}
}
catch (ArgumentException ex) when (ex.Message.Contains("参数无效"))
{
// 特别处理"参数无效"异常
Console.WriteLine($"图像参数无效异常: {ex.Message}");
try { safeImage.Dispose(); } catch {}
try { image.Dispose(); } catch {}
// 尝试设置旧图像回来,如果可能的话
if (oldImage != null)
{
try
{
imageBox.Image = oldImage;
// 获取当前的InfoImage引用
lock (_infoImageLock)
{
if (_infoImage != null)
{
infoImage = (Image)_infoImage.Clone();
}
}
// 在缓冲区上绘制图像 - 按照用户要求的步骤执行
lock (_imageBuffer)
{
using (Graphics g = Graphics.FromImage(_imageBuffer))
{
// 清除缓冲区背景为黑色
g.Clear(Color.Black);
// 步骤1先将LastImage绘制到全局缓冲
if (lastImage != null)
{
// 保持原始图像比例,居中显示
float scale = Math.Min((float)BUFFER_WIDTH / lastImage.Width, (float)BUFFER_HEIGHT / lastImage.Height);
int scaledWidth = (int)(lastImage.Width * scale);
int scaledHeight = (int)(lastImage.Height * scale);
int x = (BUFFER_WIDTH - scaledWidth) / 2;
int y = (BUFFER_HEIGHT - scaledHeight) / 2;
g.DrawImage(lastImage, x, y, scaledWidth, scaledHeight);
}
catch
{
// 如果设置旧图像也失败,释放它
try { oldImage.Dispose(); } catch {}
// 步骤2再将InfoImage绘制到缓冲
if (infoImage != null)
{
g.DrawImage(infoImage, 0, 0);
}
else if (_isDisplayingInfo)
{
// 如果没有InfoImage但需要显示信息则绘制默认信息
using (Font font = new Font("微软雅黑", 12, FontStyle.Bold))
using (Brush textBrush = new SolidBrush(Color.Red))
{
g.DrawString("测试信息", font, textBrush, 10, 10);
}
}
// 检查是否需要绘制暂停状态的mask信息
bool isPaused = pauseImageUpdateToolStripMenuItem.Text == "恢复图像更新";
if (isPaused)
{
// 绘制半透明黑色背景
using (Brush brush = new SolidBrush(Color.FromArgb(100, Color.Black)))
{
g.FillRectangle(brush, 0, 0, BUFFER_WIDTH, BUFFER_HEIGHT);
}
// 绘制"暂停"文字
using (Font font = new Font("微软雅黑", 48, FontStyle.Bold))
using (Brush textBrush = new SolidBrush(Color.White))
{
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
g.DrawString("暂停", font, textBrush,
new Rectangle(0, 0, BUFFER_WIDTH, BUFFER_HEIGHT), format);
}
}
}
}
// 步骤3创建缓冲区的副本用于显示避免在显示时锁定缓冲区
Bitmap displayImage = null;
lock (_imageBuffer)
{
displayImage = (Bitmap)_imageBuffer.Clone();
}
// 将全局缓冲一次性绘制到图像框的bitmap
imageBox.Image = displayImage;
if (lastImage != null)
{
Console.WriteLine($"图像更新成功: {lastImage.Width}x{lastImage.Height}");
}
}
catch (ArgumentException ex) when (ex.Message.Contains("参数无效"))
{
// 特别处理"参数无效"异常
Console.WriteLine($"图像参数无效异常: {ex.Message}");
// 尝试设置旧图像回来,如果可能的话
if (oldImage != null && !this.IsDisposed && imageBox != null && !imageBox.IsDisposed)
{
try
{
imageBox.Image = oldImage;
}
catch
{
// 如果设置旧图像也失败,释放它
try { oldImage.Dispose(); } catch {}
}
}
}
catch (Exception ex)
{
{
Console.WriteLine($"更新图像UI异常: {ex.Message}");
// 确保在任何异常情况下都释放所有图像资源
try { safeImage.Dispose(); } catch {}
try { image.Dispose(); } catch {}
}
finally
{
// 确保在任何情况下都释放资源
if (lastImage != null) { try { lastImage.Dispose(); } catch { } }
if (infoImage != null) { try { infoImage.Dispose(); } catch { } }
if (oldImage != null && oldImage != imageBox.Image) { try { oldImage.Dispose(); } catch { } }
}
}
@@ -1383,11 +1376,62 @@ namespace JoyD.Windows.CS.Toprie
// 无论是否在设计模式下,都需要释放图像资源
if (imageBox != null && !imageBox.IsDisposed && imageBox.Image != null)
{
{
imageBox.Image.Dispose();
imageBox.Image = null;
}
// 释放图像缓冲区资源
if (_imageBuffer != null)
{
try
{
_imageBuffer.Dispose();
_imageBuffer = null;
Console.WriteLine("图像缓冲区资源已释放");
}
catch (Exception ex)
{
Console.WriteLine($"清理ImageBuffer资源异常: {ex.Message}");
}
}
// 释放LastImage资源
lock (_lastImageLock)
{
if (_lastImage != null)
{
try
{
_lastImage.Dispose();
_lastImage = null;
Console.WriteLine("LastImage资源已释放");
}
catch (Exception ex)
{
Console.WriteLine($"清理LastImage资源异常: {ex.Message}");
}
}
}
// 释放InfoImage资源
lock (_infoImageLock)
{
if (_infoImage != null)
{
try
{
_infoImage.Dispose();
_infoImage = null;
Console.WriteLine("InfoImage资源已释放");
}
catch (Exception ex)
{
Console.WriteLine($"清理InfoImage资源异常: {ex.Message}");
}
}
}
// 释放组件资源
if (components != null)
{