improve:截图

This commit is contained in:
2025-09-13 14:16:11 +08:00
parent 98d4a4213c
commit c54d140107
3 changed files with 518 additions and 63 deletions
+103 -24
View File
@@ -154,51 +154,91 @@ namespace Ink_Canvas.Helpers
}
/// <summary>
/// 获取当前帧的Bitmap
/// </summary>
public Bitmap GetCurrentFrame()
{
lock (_frameLock)
{
return _currentFrame?.Clone() as Bitmap;
}
}
/// <summary>
/// 获取当前帧的BitmapSourceWPF格式)
/// 获取当前帧的BitmapSource(WPF格式),直接返回可用的WPF位图
/// </summary>
public BitmapSource GetCurrentFrameAsBitmapSource()
{
lock (_frameLock)
{
if (_currentFrame == null)
{
LogHelper.WriteLogToFile("GetCurrentFrameAsBitmapSource: _currentFrame为null");
return null;
}
try
{
using (var memory = new MemoryStream())
LogHelper.WriteLogToFile($"GetCurrentFrameAsBitmapSource: 开始处理帧,类型={_currentFrame.GetType().FullName}");
LogHelper.WriteLogToFile($"GetCurrentFrameAsBitmapSource: 帧HashCode={_currentFrame.GetHashCode()}");
// 验证当前帧的有效性
var width = _currentFrame.Width;
var height = _currentFrame.Height;
LogHelper.WriteLogToFile($"GetCurrentFrameAsBitmapSource: 当前帧尺寸={width}x{height}");
// 验证位图有效性
if (width <= 0 || height <= 0)
{
_currentFrame.Save(memory, ImageFormat.Png);
memory.Position = 0;
LogHelper.WriteLogToFile("当前帧无效: 尺寸为0", LogHelper.LogType.Warning);
return null;
}
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = memory;
bitmapImage.EndInit();
bitmapImage.Freeze();
// 使用更安全的方法转换位图
var bitmapData = _currentFrame.LockBits(
new Rectangle(0, 0, width, height),
ImageLockMode.ReadOnly,
_currentFrame.PixelFormat);
return bitmapImage;
try
{
// 根据像素格式选择合适的WPF像素格式
System.Windows.Media.PixelFormat wpfPixelFormat;
switch (_currentFrame.PixelFormat)
{
case PixelFormat.Format24bppRgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr24;
break;
case PixelFormat.Format32bppArgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgra32;
break;
case PixelFormat.Format32bppRgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr32;
break;
default:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr24;
break;
}
var bitmapSource = System.Windows.Media.Imaging.BitmapSource.Create(
bitmapData.Width,
bitmapData.Height,
_currentFrame.HorizontalResolution,
_currentFrame.VerticalResolution,
wpfPixelFormat,
null,
bitmapData.Scan0,
bitmapData.Stride * bitmapData.Height,
bitmapData.Stride);
bitmapSource.Freeze();
LogHelper.WriteLogToFile($"GetCurrentFrameAsBitmapSource: 成功创建BitmapSource");
return bitmapSource;
}
finally
{
_currentFrame.UnlockBits(bitmapData);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"转换帧为BitmapSource失败: {ex.Message}", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"异常详情: {ex}", LogHelper.LogType.Error);
return null;
}
}
}
/// <summary>
/// 视频源新帧事件处理
/// </summary>
@@ -206,13 +246,52 @@ namespace Ink_Canvas.Helpers
{
try
{
LogHelper.WriteLogToFile($"VideoSource_NewFrame: 接收到新帧");
lock (_frameLock)
{
// 释放之前的帧
_currentFrame?.Dispose();
// 克隆新帧
_currentFrame = eventArgs.Frame.Clone() as Bitmap;
// 创建新的位图,避免Clone的问题
var sourceFrame = eventArgs.Frame;
LogHelper.WriteLogToFile($"VideoSource_NewFrame: 源帧类型={sourceFrame?.GetType().FullName}");
LogHelper.WriteLogToFile($"VideoSource_NewFrame: 源帧HashCode={sourceFrame?.GetHashCode()}");
if (sourceFrame != null)
{
try
{
var width = sourceFrame.Width;
var height = sourceFrame.Height;
LogHelper.WriteLogToFile($"VideoSource_NewFrame: 源帧尺寸={width}x{height}");
if (width > 0 && height > 0)
{
_currentFrame = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (var graphics = Graphics.FromImage(_currentFrame))
{
graphics.DrawImage(sourceFrame, 0, 0);
}
LogHelper.WriteLogToFile($"VideoSource_NewFrame: 成功创建新帧,HashCode={_currentFrame.GetHashCode()}");
}
else
{
LogHelper.WriteLogToFile($"VideoSource_NewFrame: 源帧尺寸无效");
_currentFrame = null;
}
}
catch (Exception frameEx)
{
LogHelper.WriteLogToFile($"VideoSource_NewFrame: 处理源帧失败: {frameEx.Message}", LogHelper.LogType.Error);
_currentFrame = null;
}
}
else
{
LogHelper.WriteLogToFile($"VideoSource_NewFrame: 源帧为null");
_currentFrame = null;
}
}
// 在UI线程中触发事件
+335 -15
View File
@@ -28,12 +28,14 @@ namespace Ink_Canvas
public Rectangle Area;
public List<Point> Path;
public Bitmap CameraImage;
public System.Windows.Media.Imaging.BitmapSource CameraBitmapSource;
public ScreenshotResult(Rectangle area, List<Point> path = null, Bitmap cameraImage = null)
public ScreenshotResult(Rectangle area, List<Point> path = null, Bitmap cameraImage = null, System.Windows.Media.Imaging.BitmapSource cameraBitmapSource = null)
{
Area = area;
Path = path;
CameraImage = cameraImage;
CameraBitmapSource = cameraBitmapSource;
}
}
@@ -60,9 +62,14 @@ namespace Ink_Canvas
if (screenshotResult.HasValue)
{
// 检查是否是摄像头截图
if (screenshotResult.Value.CameraImage != null)
if (screenshotResult.Value.CameraBitmapSource != null)
{
// 摄像头截图
// 摄像头截图(使用BitmapSource
await InsertBitmapSourceToCanvas(screenshotResult.Value.CameraBitmapSource);
}
else if (screenshotResult.Value.CameraImage != null)
{
// 摄像头截图(使用Bitmap
await InsertScreenshotToCanvas(screenshotResult.Value.CameraImage);
}
else if (screenshotResult.Value.Area.Width > 0 && screenshotResult.Value.Area.Height > 0)
@@ -164,7 +171,16 @@ namespace Ink_Canvas
if (selectorWindow.ShowDialog() == true)
{
// 检查是否是摄像头截图
if (selectorWindow.CameraImage != null)
if (selectorWindow.CameraBitmapSource != null)
{
result = new ScreenshotResult(
Rectangle.Empty, // 摄像头截图不需要区域
null, // 摄像头截图不需要路径
null, // 不再使用Bitmap
selectorWindow.CameraBitmapSource // 摄像头BitmapSource
);
}
else if (selectorWindow.CameraImage != null)
{
result = new ScreenshotResult(
Rectangle.Empty, // 摄像头截图不需要区域
@@ -236,8 +252,40 @@ namespace Ink_Canvas
{
try
{
// 验证位图有效性
if (bitmap == null)
{
ShowNotification("截图数据无效");
return;
}
// 添加详细的位图调试信息
LogHelper.WriteLogToFile($"InsertScreenshotToCanvas: 开始处理位图");
LogHelper.WriteLogToFile($"位图对象类型: {bitmap.GetType().FullName}");
LogHelper.WriteLogToFile($"位图对象HashCode: {bitmap.GetHashCode()}");
// 检查位图是否已被释放
try
{
var testWidth = bitmap.Width;
var testHeight = bitmap.Height;
LogHelper.WriteLogToFile($"位图尺寸验证成功: {testWidth}x{testHeight}");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"位图验证失败: {ex.Message}", LogHelper.LogType.Error);
ShowNotification("截图数据已损坏");
return;
}
// 将Bitmap转换为WPF BitmapSource
var bitmapSource = ConvertBitmapToBitmapSource(bitmap);
if (bitmapSource == null)
{
ShowNotification("转换截图失败");
return;
}
// 创建WPF Image控件
var image = new Image
@@ -293,6 +341,67 @@ namespace Ink_Canvas
}
}
// 将BitmapSource插入到画布(用于摄像头截图)
private async Task InsertBitmapSourceToCanvas(System.Windows.Media.Imaging.BitmapSource bitmapSource)
{
try
{
LogHelper.WriteLogToFile($"InsertBitmapSourceToCanvas: 开始处理BitmapSource");
// 创建WPF Image控件
var image = new Image
{
Source = bitmapSource,
Stretch = Stretch.Uniform
};
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
// 生成唯一名称
string timestamp = "camera_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
// 初始化TransformGroup
InitializeScreenshotTransform(image);
// 设置截图属性,避免被InkCanvas选择系统处理
image.IsHitTestVisible = true;
image.Focusable = false;
// 初始化InkCanvas选择设置
InitializeInkCanvasSelectionSettings();
// 等待图片加载完成后再进行居中处理
image.Loaded += (sender, e) =>
{
// 确保在UI线程中执行
Dispatcher.BeginInvoke(new Action(() =>
{
CenterAndScaleScreenshot(image);
// 绑定事件处理器
BindScreenshotEvents(image);
}), DispatcherPriority.Loaded);
};
// 添加到画布
inkCanvas.Children.Add(image);
// 提交历史记录
timeMachine.CommitElementInsertHistory(image);
// 插入图片后切换到选择模式并刷新浮动栏高光显示
SetCurrentToolMode(InkCanvasEditingMode.Select);
UpdateCurrentToolMode("select");
HideSubPanels("select");
ShowNotification("摄像头截图已插入到画布");
}
catch (Exception ex)
{
ShowNotification($"插入摄像头截图失败: {ex.Message}");
LogHelper.WriteLogToFile($"插入摄像头截图失败: {ex.Message}", LogHelper.LogType.Error);
}
}
// 初始化截图的TransformGroup
private void InitializeScreenshotTransform(Image image)
{
@@ -501,24 +610,235 @@ namespace Ink_Canvas
{
try
{
using (var memory = new MemoryStream())
// 验证位图有效性
if (bitmap == null)
{
bitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
LogHelper.WriteLogToFile("位图无效: 位图为null", LogHelper.LogType.Warning);
return null;
}
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = memory;
bitmapImage.EndInit();
bitmapImage.Freeze();
// 尝试访问位图属性,如果失败说明位图已损坏
int width, height;
try
{
width = bitmap.Width;
height = bitmap.Height;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"位图已损坏,无法访问属性: {ex.Message}", LogHelper.LogType.Error);
return null;
}
return bitmapImage;
if (width <= 0 || height <= 0)
{
LogHelper.WriteLogToFile($"位图无效: 尺寸为{width}x{height}", LogHelper.LogType.Warning);
return null;
}
LogHelper.WriteLogToFile($"开始转换位图: 尺寸={width}x{height}, 格式={bitmap.PixelFormat}, DPI={bitmap.HorizontalResolution}x{bitmap.VerticalResolution}");
// 使用更安全的方法转换位图
var bitmapData = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly,
bitmap.PixelFormat);
try
{
LogHelper.WriteLogToFile($"LockBits成功: Stride={bitmapData.Stride}, Scan0={bitmapData.Scan0}");
// 根据像素格式选择合适的WPF像素格式
System.Windows.Media.PixelFormat wpfPixelFormat;
switch (bitmap.PixelFormat)
{
case PixelFormat.Format24bppRgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr24;
LogHelper.WriteLogToFile("使用Bgr24像素格式");
break;
case PixelFormat.Format32bppArgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgra32;
LogHelper.WriteLogToFile("使用Bgra32像素格式");
break;
case PixelFormat.Format32bppRgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr32;
LogHelper.WriteLogToFile("使用Bgr32像素格式");
break;
default:
// 默认使用Bgr24,如果格式不匹配则转换
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr24;
LogHelper.WriteLogToFile($"未知像素格式{bitmap.PixelFormat},使用默认Bgr24");
break;
}
var bitmapSource = System.Windows.Media.Imaging.BitmapSource.Create(
bitmapData.Width,
bitmapData.Height,
bitmap.HorizontalResolution,
bitmap.VerticalResolution,
wpfPixelFormat,
null,
bitmapData.Scan0,
bitmapData.Stride * bitmapData.Height,
bitmapData.Stride);
bitmapSource.Freeze();
LogHelper.WriteLogToFile("位图转换成功");
return bitmapSource;
}
finally
{
bitmap.UnlockBits(bitmapData);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"转换位图失败: {ex.Message}", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"直接转换位图失败: {ex.Message}", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"异常详情: {ex}", LogHelper.LogType.Error);
// 尝试使用备用方法:内存流转换
try
{
LogHelper.WriteLogToFile("尝试使用内存流方式转换位图", LogHelper.LogType.Info);
return ConvertBitmapToBitmapSourceFallback(bitmap);
}
catch (Exception fallbackEx)
{
LogHelper.WriteLogToFile($"备用转换方法也失败: {fallbackEx.Message}", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"备用方法异常详情: {fallbackEx}", LogHelper.LogType.Error);
// 最后尝试:使用最简单的转换方法
try
{
LogHelper.WriteLogToFile("尝试最简单的转换方法", LogHelper.LogType.Info);
return ConvertBitmapToBitmapSourceSimple(bitmap);
}
catch (Exception simpleEx)
{
LogHelper.WriteLogToFile($"简单转换方法也失败: {simpleEx.Message}", LogHelper.LogType.Error);
throw;
}
}
}
}
// 备用的位图转换方法(使用内存流)
private BitmapSource ConvertBitmapToBitmapSourceFallback(Bitmap bitmap)
{
try
{
LogHelper.WriteLogToFile("开始备用转换方法");
// 验证位图有效性
if (bitmap == null)
{
LogHelper.WriteLogToFile("备用方法:位图为null", LogHelper.LogType.Warning);
return null;
}
if (bitmap.Width <= 0 || bitmap.Height <= 0)
{
LogHelper.WriteLogToFile($"备用方法:位图尺寸无效 {bitmap.Width}x{bitmap.Height}", LogHelper.LogType.Warning);
return null;
}
LogHelper.WriteLogToFile($"备用方法:位图尺寸={bitmap.Width}x{bitmap.Height}, 格式={bitmap.PixelFormat}");
// 创建一个新的位图,确保格式正确
using (var convertedBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format24bppRgb))
{
LogHelper.WriteLogToFile("创建转换位图成功");
using (var graphics = Graphics.FromImage(convertedBitmap))
{
LogHelper.WriteLogToFile("开始绘制到转换位图");
graphics.DrawImage(bitmap, 0, 0);
LogHelper.WriteLogToFile("绘制完成");
}
using (var memory = new MemoryStream())
{
LogHelper.WriteLogToFile("开始保存到内存流");
convertedBitmap.Save(memory, ImageFormat.Png);
LogHelper.WriteLogToFile($"保存完成,内存流大小={memory.Length}");
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = memory;
bitmapImage.EndInit();
bitmapImage.Freeze();
LogHelper.WriteLogToFile("备用转换方法成功");
return bitmapImage;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"备用转换方法失败: {ex.Message}", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"备用方法异常详情: {ex}", LogHelper.LogType.Error);
throw;
}
}
// 最简单的位图转换方法
private BitmapSource ConvertBitmapToBitmapSourceSimple(Bitmap bitmap)
{
try
{
LogHelper.WriteLogToFile("开始简单转换方法");
if (bitmap == null)
{
LogHelper.WriteLogToFile("简单方法:位图为null", LogHelper.LogType.Warning);
return null;
}
LogHelper.WriteLogToFile($"简单方法:位图尺寸={bitmap.Width}x{bitmap.Height}");
// 使用最基础的方法:直接保存为PNG然后加载
var tempFile = System.IO.Path.GetTempFileName() + ".png";
try
{
LogHelper.WriteLogToFile($"保存临时文件到: {tempFile}");
bitmap.Save(tempFile, ImageFormat.Png);
LogHelper.WriteLogToFile("开始加载临时文件");
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(tempFile);
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
LogHelper.WriteLogToFile("简单转换方法成功");
return bitmapImage;
}
finally
{
// 清理临时文件
try
{
if (System.IO.File.Exists(tempFile))
{
System.IO.File.Delete(tempFile);
LogHelper.WriteLogToFile("临时文件已删除");
}
}
catch (Exception deleteEx)
{
LogHelper.WriteLogToFile($"删除临时文件失败: {deleteEx.Message}", LogHelper.LogType.Warning);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"简单转换方法失败: {ex.Message}", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"简单方法异常详情: {ex}", LogHelper.LogType.Error);
throw;
}
}
@@ -49,6 +49,7 @@ namespace Ink_Canvas
public DrawingRectangle? SelectedArea { get; private set; }
public List<WpfPoint> SelectedPath { get; private set; }
public Bitmap CameraImage { get; private set; }
public System.Windows.Media.Imaging.BitmapSource CameraBitmapSource { get; private set; }
public ScreenshotSelectorWindow()
{
@@ -156,18 +157,35 @@ namespace Ink_Canvas
{
if (_isCameraMode && CameraPreviewImage != null && frame != null)
{
// 克隆帧以避免在转换过程中被释放
using (var clonedFrame = frame.Clone() as Bitmap)
try
{
if (clonedFrame != null)
// 验证帧的有效性
if (frame.Width <= 0 || frame.Height <= 0)
{
var bitmapSource = ConvertBitmapToBitmapSource(clonedFrame);
if (bitmapSource != null)
{
CameraPreviewImage.Source = bitmapSource;
CameraStatusText.Text = "摄像头已连接";
}
LogHelper.WriteLogToFile($"无效的摄像头帧: {frame.Width}x{frame.Height}", LogHelper.LogType.Warning);
return;
}
// 创建新的位图,避免Clone的问题
var clonedFrame = new Bitmap(frame.Width, frame.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (var graphics = System.Drawing.Graphics.FromImage(clonedFrame))
{
graphics.DrawImage(frame, 0, 0);
}
var bitmapSource = ConvertBitmapToBitmapSource(clonedFrame);
if (bitmapSource != null)
{
CameraPreviewImage.Source = bitmapSource;
CameraStatusText.Text = "摄像头已连接";
}
// 释放临时位图
clonedFrame.Dispose();
}
catch (Exception frameEx)
{
LogHelper.WriteLogToFile($"处理摄像头帧时出错: {frameEx.Message}", LogHelper.LogType.Error);
}
}
}));
@@ -197,19 +215,57 @@ namespace Ink_Canvas
{
try
{
using (var memory = new System.IO.MemoryStream())
// 验证位图有效性
if (bitmap == null || bitmap.Width <= 0 || bitmap.Height <= 0)
{
bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
memory.Position = 0;
LogHelper.WriteLogToFile("位图无效: 空位图或尺寸为0", LogHelper.LogType.Warning);
return null;
}
var bitmapImage = new System.Windows.Media.Imaging.BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = System.Windows.Media.Imaging.BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = memory;
bitmapImage.EndInit();
bitmapImage.Freeze();
// 使用更安全的方法转换位图
var bitmapData = bitmap.LockBits(
new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
bitmap.PixelFormat);
return bitmapImage;
try
{
// 根据像素格式选择合适的WPF像素格式
System.Windows.Media.PixelFormat wpfPixelFormat;
switch (bitmap.PixelFormat)
{
case System.Drawing.Imaging.PixelFormat.Format24bppRgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr24;
break;
case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgra32;
break;
case System.Drawing.Imaging.PixelFormat.Format32bppRgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr32;
break;
default:
// 默认使用Bgr24,如果格式不匹配则转换
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr24;
break;
}
var bitmapSource = System.Windows.Media.Imaging.BitmapSource.Create(
bitmapData.Width,
bitmapData.Height,
bitmap.HorizontalResolution,
bitmap.VerticalResolution,
wpfPixelFormat,
null,
bitmapData.Scan0,
bitmapData.Stride * bitmapData.Height,
bitmapData.Stride);
bitmapSource.Freeze();
return bitmapSource;
}
finally
{
bitmap.UnlockBits(bitmapData);
}
}
catch (Exception ex)
@@ -430,12 +486,12 @@ namespace Ink_Canvas
{
if (_cameraService != null && _cameraService.IsCapturing)
{
// 获取当前帧
var currentFrame = _cameraService.GetCurrentFrame();
if (currentFrame != null)
// 直接获取BitmapSource,避免Bitmap传递问题
var bitmapSource = _cameraService.GetCurrentFrameAsBitmapSource();
if (bitmapSource != null)
{
// 保存摄像头图像
CameraImage = currentFrame.Clone() as Bitmap;
// 保存BitmapSource而不是Bitmap
CameraBitmapSource = bitmapSource;
// 停止摄像头预览
_cameraService.StopPreview();