1c8bdcf352
* 调整截图按钮默认行为为选区/白板全屏 * 截图选择器新增添加到白板按钮并支持新建白板页插入 * 修复添加到白板不生效:改为剪贴板复制粘贴插入 * 修复添加到白板截图时序:先截到内存再进白板 * Fix screenshot insert flow honoring add-to-whiteboard option * Refine screenshot selector freehand confirm and blank double-click full select * Fix freehand whiteboard insert alpha transparency * Fix screenshot insert notification text for whiteboard flow * Adjust screenshot selection: right-click/double-click full-select and keep selector on single click * Show unmasked freehand selection area like rectangle mode * Remove freehand cutout mask rendering from screenshot selector * Restore freehand selected-area unmask behavior in selector * fix: always restore window visibility after area screenshot * fix: allow add-to-whiteboard after full-screen selection
1506 lines
55 KiB
C#
1506 lines
55 KiB
C#
using Ink_Canvas.Helpers;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Forms;
|
|
using System.Windows.Input;
|
|
using System.Windows.Media;
|
|
using System.Windows.Shapes;
|
|
using System.Windows.Threading;
|
|
using Brushes = System.Windows.Media.Brushes;
|
|
using Color = System.Windows.Media.Color;
|
|
using DrawingRectangle = System.Drawing.Rectangle;
|
|
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
|
|
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
|
|
using WpfCanvas = System.Windows.Controls.Canvas;
|
|
using WpfPoint = System.Windows.Point;
|
|
|
|
namespace Ink_Canvas
|
|
{
|
|
public partial class ScreenshotSelectorWindow : Window
|
|
{
|
|
private bool _isSelecting;
|
|
private bool _isFreehandMode;
|
|
private bool _isAdjusting;
|
|
private bool _isMoving;
|
|
private bool _isCameraMode;
|
|
private WpfPoint _startPoint;
|
|
private WpfPoint _currentPoint;
|
|
private WpfPoint _lastMousePosition;
|
|
private List<WpfPoint> _freehandPoints;
|
|
private Polyline _freehandPolyline;
|
|
private Rect _currentSelection;
|
|
private ControlPointType _activeControlPoint = ControlPointType.None;
|
|
private CameraService _cameraService;
|
|
private Bitmap _capturedCameraImage = null;
|
|
private DateTime _lastBlankClickTime = DateTime.MinValue;
|
|
private WpfPoint _lastBlankClickPosition;
|
|
|
|
private const int DoubleClickTimeThresholdMs = 300; // 双击判定时间阈值(常见范围 200~500ms)
|
|
private const double DoubleClickDistanceThresholdPx = 12; // 双击判定位置阈值(像素)
|
|
|
|
// 控制点类型枚举
|
|
private enum ControlPointType
|
|
{
|
|
None,
|
|
TopLeft, TopRight, BottomLeft, BottomRight,
|
|
Top, Bottom, Left, Right,
|
|
Move
|
|
}
|
|
|
|
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 bool ShouldAddToWhiteboard { get; private set; }
|
|
|
|
public ScreenshotSelectorWindow()
|
|
{
|
|
InitializeComponent();
|
|
|
|
// 设置窗口覆盖所有屏幕
|
|
SetupFullScreenOverlay();
|
|
|
|
// 初始化自由绘制模式
|
|
InitializeFreehandMode();
|
|
|
|
// 绑定控制点事件
|
|
BindControlPointEvents();
|
|
|
|
// 初始化按钮状态
|
|
InitializeButtonStates();
|
|
|
|
// 初始化摄像头服务
|
|
InitializeCameraService();
|
|
|
|
// 隐藏提示文字的定时器
|
|
var timer = new DispatcherTimer();
|
|
timer.Interval = TimeSpan.FromSeconds(5);
|
|
timer.Tick += (s, e) =>
|
|
{
|
|
HintTextBorder.Visibility = Visibility.Collapsed;
|
|
timer.Stop();
|
|
};
|
|
timer.Start();
|
|
}
|
|
|
|
private void InitializeFreehandMode()
|
|
{
|
|
_freehandPoints = new List<WpfPoint>();
|
|
_freehandPolyline = new Polyline
|
|
{
|
|
Stroke = Brushes.White,
|
|
StrokeThickness = 1,
|
|
Fill = Brushes.Transparent
|
|
};
|
|
SelectionCanvas.Children.Add(_freehandPolyline);
|
|
}
|
|
|
|
private void InitializeButtonStates()
|
|
{
|
|
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
CameraModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
}
|
|
|
|
private void InitializeCameraService()
|
|
{
|
|
try
|
|
{
|
|
// 从设置中加载摄像头配置
|
|
var cameraSettings = MainWindow.Settings.Camera;
|
|
_cameraService = new CameraService(
|
|
cameraSettings.RotationAngle,
|
|
cameraSettings.ResolutionWidth,
|
|
cameraSettings.ResolutionHeight);
|
|
_cameraService.FrameReceived += CameraService_FrameReceived;
|
|
_cameraService.ErrorOccurred += CameraService_ErrorOccurred;
|
|
|
|
// 初始化摄像头选择下拉框
|
|
RefreshCameraComboBox();
|
|
|
|
// 初始化旋转和分辨率显示
|
|
InitializeCameraControls();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogHelper.WriteLogToFile($"初始化摄像头服务失败: {ex.Message}", LogHelper.LogType.Error);
|
|
}
|
|
}
|
|
|
|
private void InitializeCameraControls()
|
|
{
|
|
if (_cameraService != null)
|
|
{
|
|
// 更新旋转角度显示
|
|
UpdateRotationDisplay();
|
|
|
|
// 设置分辨率下拉框
|
|
var currentResolution = $"{_cameraService.ResolutionWidth}x{_cameraService.ResolutionHeight}";
|
|
foreach (ComboBoxItem item in ResolutionComboBox.Items)
|
|
{
|
|
if (item.Tag?.ToString() == $"{_cameraService.ResolutionWidth},{_cameraService.ResolutionHeight}")
|
|
{
|
|
ResolutionComboBox.SelectedItem = item;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void RefreshCameraComboBox()
|
|
{
|
|
try
|
|
{
|
|
CameraSelectionComboBox.Items.Clear();
|
|
|
|
if (_cameraService.HasAvailableCameras())
|
|
{
|
|
var cameraNames = _cameraService.GetCameraNames();
|
|
foreach (var name in cameraNames)
|
|
{
|
|
CameraSelectionComboBox.Items.Add(name);
|
|
}
|
|
|
|
if (cameraNames.Count > 0)
|
|
{
|
|
CameraSelectionComboBox.SelectedIndex = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CameraSelectionComboBox.Items.Add("未找到摄像头设备");
|
|
CameraSelectionComboBox.SelectedIndex = 0;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogHelper.WriteLogToFile($"刷新摄像头列表失败: {ex.Message}", LogHelper.LogType.Error);
|
|
}
|
|
}
|
|
|
|
private void CameraService_FrameReceived(object sender, Bitmap frame)
|
|
{
|
|
try
|
|
{
|
|
Dispatcher.BeginInvoke(new Action(() =>
|
|
{
|
|
if (_isCameraMode && CameraPreviewImage != null && frame != null)
|
|
{
|
|
try
|
|
{
|
|
// 验证帧的有效性
|
|
if (frame.Width <= 0 || frame.Height <= 0)
|
|
return;
|
|
|
|
// 创建新的位图,避免Clone的问题
|
|
var clonedFrame = new Bitmap(frame.Width, frame.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
|
|
using (var graphics = 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);
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogHelper.WriteLogToFile($"处理摄像头帧失败: {ex.Message}", LogHelper.LogType.Error);
|
|
}
|
|
}
|
|
|
|
private void CameraService_ErrorOccurred(object sender, string error)
|
|
{
|
|
try
|
|
{
|
|
Dispatcher.BeginInvoke(new Action(() =>
|
|
{
|
|
CameraStatusText.Text = $"摄像头错误: {error}";
|
|
}));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogHelper.WriteLogToFile($"处理摄像头错误失败: {ex.Message}", LogHelper.LogType.Error);
|
|
}
|
|
}
|
|
|
|
private System.Windows.Media.Imaging.BitmapSource ConvertBitmapToBitmapSource(Bitmap bitmap)
|
|
{
|
|
try
|
|
{
|
|
// 验证位图有效性
|
|
if (bitmap == null || bitmap.Width <= 0 || bitmap.Height <= 0)
|
|
return null;
|
|
|
|
// 使用更安全的方法转换位图
|
|
var bitmapData = bitmap.LockBits(
|
|
new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
|
System.Drawing.Imaging.ImageLockMode.ReadOnly,
|
|
bitmap.PixelFormat);
|
|
|
|
try
|
|
{
|
|
// 根据像素格式选择合适的WPF像素格式
|
|
PixelFormat wpfPixelFormat;
|
|
switch (bitmap.PixelFormat)
|
|
{
|
|
case System.Drawing.Imaging.PixelFormat.Format24bppRgb:
|
|
wpfPixelFormat = PixelFormats.Bgr24;
|
|
break;
|
|
case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
|
|
wpfPixelFormat = PixelFormats.Bgra32;
|
|
break;
|
|
case System.Drawing.Imaging.PixelFormat.Format32bppRgb:
|
|
wpfPixelFormat = PixelFormats.Bgr32;
|
|
break;
|
|
default:
|
|
wpfPixelFormat = 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)
|
|
{
|
|
LogHelper.WriteLogToFile($"转换位图失败: {ex.Message}", LogHelper.LogType.Error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private void BindControlPointEvents()
|
|
{
|
|
// 绑定所有控制点的鼠标事件
|
|
var controlPoints = new[]
|
|
{
|
|
TopLeftControl, TopRightControl, BottomLeftControl, BottomRightControl,
|
|
TopControl, BottomControl, LeftControl, RightControl
|
|
};
|
|
|
|
foreach (var control in controlPoints)
|
|
{
|
|
control.MouseLeftButtonDown += ControlPoint_MouseLeftButtonDown;
|
|
control.MouseLeftButtonUp += ControlPoint_MouseLeftButtonUp;
|
|
control.MouseMove += ControlPoint_MouseMove;
|
|
|
|
// 确保控制点能够接收鼠标事件
|
|
control.IsHitTestVisible = true;
|
|
control.Focusable = false;
|
|
|
|
// 设置控制点的Z-index,确保它们在最上层
|
|
WpfCanvas.SetZIndex(control, 1003);
|
|
}
|
|
}
|
|
|
|
private void SetupFullScreenOverlay()
|
|
{
|
|
// 获取所有屏幕的虚拟屏幕边界
|
|
var virtualScreen = SystemInformation.VirtualScreen;
|
|
|
|
// 转换为WPF坐标系统
|
|
var dpiScale = GetDpiScale();
|
|
|
|
Left = virtualScreen.Left / dpiScale;
|
|
Top = virtualScreen.Top / dpiScale;
|
|
Width = virtualScreen.Width / dpiScale;
|
|
Height = virtualScreen.Height / dpiScale;
|
|
}
|
|
|
|
private double GetDpiScale()
|
|
{
|
|
var source = PresentationSource.FromVisual(this);
|
|
if (source?.CompositionTarget != null)
|
|
{
|
|
return source.CompositionTarget.TransformToDevice.M11;
|
|
}
|
|
return 1.0; // 默认DPI
|
|
}
|
|
|
|
private void Window_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.Key == Key.Escape)
|
|
{
|
|
CancelSelection();
|
|
}
|
|
else if (e.Key == Key.Enter)
|
|
{
|
|
ConfirmSelection();
|
|
}
|
|
}
|
|
|
|
private void RectangleModeButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
// 重置所有选择状态
|
|
ResetSelectionState();
|
|
|
|
_isFreehandMode = false;
|
|
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
|
|
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
HintText.Text = "拖拽鼠标选择矩形区域";
|
|
HintTextBorder.Visibility = Visibility.Visible;
|
|
}
|
|
|
|
private void FreehandModeButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
// 重置所有选择状态
|
|
ResetSelectionState();
|
|
|
|
_isFreehandMode = true;
|
|
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
|
|
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
HintText.Text = "按住鼠标左键自由绘制,松开后可继续调整或重新绘制,确认后再截图";
|
|
HintTextBorder.Visibility = Visibility.Visible;
|
|
}
|
|
|
|
private void FullScreenButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
// 重置所有选择状态
|
|
ResetSelectionState();
|
|
|
|
// 设置全屏截图模式
|
|
_isFreehandMode = false;
|
|
_isCameraMode = false;
|
|
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
|
|
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
CameraModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
|
|
// 隐藏摄像头预览
|
|
CameraPreviewBorder.Visibility = Visibility.Collapsed;
|
|
|
|
// 直接执行全屏截图
|
|
PerformFullScreenCapture();
|
|
}
|
|
|
|
private void CameraModeButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
// 重置所有选择状态
|
|
ResetSelectionState();
|
|
|
|
// 设置摄像头模式
|
|
_isFreehandMode = false;
|
|
_isCameraMode = true;
|
|
CameraModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
|
|
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
|
|
// 显示摄像头预览
|
|
CameraPreviewBorder.Visibility = Visibility.Visible;
|
|
HintText.Text = "摄像头预览模式,点击确认截图按钮进行截图";
|
|
HintTextBorder.Visibility = Visibility.Visible;
|
|
|
|
// 启动摄像头预览
|
|
if (_cameraService != null && _cameraService.HasAvailableCameras())
|
|
{
|
|
var selectedIndex = CameraSelectionComboBox.SelectedIndex;
|
|
if (selectedIndex >= 0 && selectedIndex < _cameraService.GetCameraNames().Count)
|
|
{
|
|
_cameraService.StartPreview(selectedIndex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CameraStatusText.Text = "未找到摄像头设备";
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogHelper.WriteLogToFile($"启动摄像头模式失败: {ex.Message}", LogHelper.LogType.Error);
|
|
CameraStatusText.Text = $"启动摄像头失败: {ex.Message}";
|
|
}
|
|
}
|
|
|
|
private void CameraSelectionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
if (_isCameraMode && _cameraService != null)
|
|
{
|
|
var selectedIndex = CameraSelectionComboBox.SelectedIndex;
|
|
if (selectedIndex >= 0 && selectedIndex < _cameraService.GetCameraNames().Count)
|
|
{
|
|
_cameraService.SwitchCamera(selectedIndex);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogHelper.WriteLogToFile($"切换摄像头失败: {ex.Message}", LogHelper.LogType.Error);
|
|
}
|
|
}
|
|
|
|
private void SwitchCameraButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
if (_cameraService != null && _cameraService.HasAvailableCameras())
|
|
{
|
|
var cameraNames = _cameraService.GetCameraNames();
|
|
if (cameraNames.Count > 1)
|
|
{
|
|
var currentIndex = CameraSelectionComboBox.SelectedIndex;
|
|
var nextIndex = (currentIndex + 1) % cameraNames.Count;
|
|
CameraSelectionComboBox.SelectedIndex = nextIndex;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogHelper.WriteLogToFile($"切换摄像头失败: {ex.Message}", LogHelper.LogType.Error);
|
|
}
|
|
}
|
|
|
|
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
ShouldAddToWhiteboard = false;
|
|
|
|
// 在自由绘制模式下,按当前自由选区执行确认
|
|
if (_isFreehandMode)
|
|
{
|
|
ConfirmFreehandSelection();
|
|
return;
|
|
}
|
|
|
|
// 在摄像头模式下,执行摄像头截图
|
|
if (_isCameraMode)
|
|
{
|
|
ConfirmCameraCapture();
|
|
return;
|
|
}
|
|
|
|
ConfirmSelection();
|
|
}
|
|
|
|
private void AddToWhiteboardButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
ShouldAddToWhiteboard = true;
|
|
|
|
// 在自由绘制模式下,按当前自由选区执行确认
|
|
if (_isFreehandMode)
|
|
{
|
|
ConfirmFreehandSelection();
|
|
return;
|
|
}
|
|
|
|
if (_isCameraMode)
|
|
{
|
|
ConfirmCameraCapture();
|
|
return;
|
|
}
|
|
|
|
ConfirmSelection();
|
|
}
|
|
|
|
private void ConfirmCameraCapture()
|
|
{
|
|
try
|
|
{
|
|
if (_cameraService != null && _cameraService.IsCapturing)
|
|
{
|
|
// 直接获取BitmapSource,避免Bitmap传递问题
|
|
var bitmapSource = _cameraService.GetCurrentFrameAsBitmapSource();
|
|
if (bitmapSource != null)
|
|
{
|
|
// 保存BitmapSource而不是Bitmap
|
|
CameraBitmapSource = bitmapSource;
|
|
|
|
// 停止摄像头预览
|
|
_cameraService.StopPreview();
|
|
|
|
// 设置结果并关闭窗口
|
|
DialogResult = true;
|
|
Close();
|
|
}
|
|
else
|
|
{
|
|
CameraStatusText.Text = "无法获取摄像头画面";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CameraStatusText.Text = "摄像头未启动";
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogHelper.WriteLogToFile($"摄像头截图失败: {ex.Message}", LogHelper.LogType.Error);
|
|
CameraStatusText.Text = $"摄像头截图失败: {ex.Message}";
|
|
}
|
|
}
|
|
|
|
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
CancelSelection();
|
|
}
|
|
|
|
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
|
{
|
|
// 检查是否点击了UI元素,如果是则不处理选择
|
|
var hitElement = e.Source as FrameworkElement;
|
|
if (hitElement != null && (
|
|
hitElement is Ellipse ||
|
|
hitElement is System.Windows.Controls.Button ||
|
|
hitElement is Border ||
|
|
hitElement is TextBlock ||
|
|
hitElement is StackPanel ||
|
|
hitElement is Separator ||
|
|
hitElement.Name == "SizeInfoBorder" ||
|
|
hitElement.Name == "HintText" ||
|
|
hitElement.Name == "AdjustModeHint" ||
|
|
hitElement.Name == "SelectionRectangle"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 如果正在调整,忽略新的选择
|
|
if (_isAdjusting) return;
|
|
|
|
// 空白区域双击可快速全选(时间阈值 300ms,位置阈值 12px)
|
|
if (!_isCameraMode)
|
|
{
|
|
var clickPoint = e.GetPosition(this);
|
|
var now = DateTime.Now;
|
|
if ((e.ClickCount >= 2 || (now - _lastBlankClickTime).TotalMilliseconds <= DoubleClickTimeThresholdMs) &&
|
|
Math.Abs(clickPoint.X - _lastBlankClickPosition.X) <= DoubleClickDistanceThresholdPx &&
|
|
Math.Abs(clickPoint.Y - _lastBlankClickPosition.Y) <= DoubleClickDistanceThresholdPx)
|
|
{
|
|
SelectFullScreenArea();
|
|
_lastBlankClickTime = DateTime.MinValue;
|
|
e.Handled = true;
|
|
return;
|
|
}
|
|
|
|
_lastBlankClickTime = now;
|
|
_lastBlankClickPosition = clickPoint;
|
|
}
|
|
|
|
// 如果正在选择,先重置状态
|
|
if (_isSelecting)
|
|
{
|
|
_isSelecting = false;
|
|
ReleaseMouseCapture();
|
|
}
|
|
|
|
// 开始新的选择
|
|
_isSelecting = true;
|
|
_startPoint = e.GetPosition(this);
|
|
_currentPoint = _startPoint;
|
|
|
|
// 隐藏提示文字
|
|
HintTextBorder.Visibility = Visibility.Collapsed;
|
|
|
|
if (_isFreehandMode)
|
|
{
|
|
// 自由绘制模式:开始绘制
|
|
_freehandPoints.Clear();
|
|
_freehandPolyline.Points.Clear();
|
|
SelectedArea = null;
|
|
SelectedPath = null;
|
|
_freehandPoints.Add(_startPoint);
|
|
_freehandPolyline.Points.Add(_startPoint);
|
|
|
|
// 确保自由绘制路径可见
|
|
_freehandPolyline.Visibility = Visibility.Visible;
|
|
UpdateFreehandSelectionMask(_freehandPolyline.Points);
|
|
}
|
|
else
|
|
{
|
|
// 矩形模式
|
|
SelectionRectangle.Visibility = Visibility.Visible;
|
|
SizeInfoBorder.Visibility = Visibility.Visible;
|
|
}
|
|
|
|
// 捕获鼠标
|
|
CaptureMouse();
|
|
|
|
if (!_isFreehandMode)
|
|
{
|
|
UpdateSelection();
|
|
}
|
|
}
|
|
|
|
private void Window_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
|
{
|
|
if (_isCameraMode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 点击到工具栏等UI元素时不触发全选
|
|
var hitElement = e.Source as FrameworkElement;
|
|
if (hitElement != null && (
|
|
hitElement is Ellipse ||
|
|
hitElement is System.Windows.Controls.Button ||
|
|
hitElement is Border ||
|
|
hitElement is TextBlock ||
|
|
hitElement is StackPanel ||
|
|
hitElement is Separator ||
|
|
hitElement.Name == "SizeInfoBorder" ||
|
|
hitElement.Name == "HintText" ||
|
|
hitElement.Name == "AdjustModeHint"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_isSelecting)
|
|
{
|
|
_isSelecting = false;
|
|
if (IsMouseCaptured)
|
|
{
|
|
ReleaseMouseCapture();
|
|
}
|
|
}
|
|
|
|
SelectFullScreenArea();
|
|
e.Handled = true;
|
|
}
|
|
|
|
private void Window_MouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
// 如果正在调整模式且正在移动,不处理窗口级别的鼠标移动
|
|
if (_isAdjusting && _isMoving)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_isSelecting)
|
|
{
|
|
_currentPoint = e.GetPosition(this);
|
|
|
|
if (_isFreehandMode)
|
|
{
|
|
// 自由绘制模式:添加点到路径
|
|
_freehandPoints.Add(_currentPoint);
|
|
_freehandPolyline.Points.Add(_currentPoint);
|
|
|
|
// 确保自由绘制路径可见
|
|
_freehandPolyline.Visibility = Visibility.Visible;
|
|
UpdateFreehandSelectionMask(_freehandPolyline.Points);
|
|
}
|
|
else
|
|
{
|
|
// 矩形模式
|
|
UpdateSelection();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Window_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
|
{
|
|
// 如果正在调整模式且正在移动,不处理窗口级别的鼠标释放
|
|
if (_isAdjusting && _isMoving)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_isSelecting)
|
|
{
|
|
_isSelecting = false;
|
|
ReleaseMouseCapture();
|
|
|
|
if (_isFreehandMode)
|
|
{
|
|
// 自由绘制模式:松开后先保留选区,等待用户点击“确认截图/添加到白板”
|
|
if (_freehandPoints.Count > 1)
|
|
{
|
|
// 创建路径副本并闭合
|
|
var pathPoints = new List<WpfPoint>(_freehandPoints);
|
|
if (pathPoints.Count > 0)
|
|
{
|
|
pathPoints.Add(_startPoint);
|
|
}
|
|
|
|
var optimizedPath = OptimizePath(pathPoints);
|
|
var bounds = CalculatePathBounds(optimizedPath);
|
|
|
|
if (bounds.Width > 5 && bounds.Height > 5)
|
|
{
|
|
SelectedPath = optimizedPath;
|
|
|
|
var dpiScale = GetDpiScale();
|
|
var virtualScreen = SystemInformation.VirtualScreen;
|
|
int screenX = (int)((bounds.X * dpiScale) + virtualScreen.Left);
|
|
int screenY = (int)((bounds.Y * dpiScale) + virtualScreen.Top);
|
|
int screenWidth = (int)(bounds.Width * dpiScale);
|
|
int screenHeight = (int)(bounds.Height * dpiScale);
|
|
|
|
SelectedArea = new DrawingRectangle(screenX, screenY, screenWidth, screenHeight);
|
|
UpdateFreehandSelectionMask(optimizedPath);
|
|
HintText.Text = "自由选区已完成,可点击确认截图/添加到白板;重新拖动可重画";
|
|
HintTextBorder.Visibility = Visibility.Visible;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 自由绘制无效则清理本次路径,允许继续重绘
|
|
_freehandPoints.Clear();
|
|
_freehandPolyline.Points.Clear();
|
|
_freehandPolyline.Visibility = Visibility.Collapsed;
|
|
TransparentSelectionMask.Visibility = Visibility.Collapsed;
|
|
OverlayRectangle.Visibility = Visibility.Visible;
|
|
SelectedArea = null;
|
|
SelectedPath = null;
|
|
HintText.Text = "自由选区过小,请重新绘制";
|
|
HintTextBorder.Visibility = Visibility.Visible;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// 矩形模式:进入调整模式
|
|
var rect = GetSelectionRectangle();
|
|
if (rect.Width > 5 && rect.Height > 5) // 最小尺寸检查
|
|
{
|
|
_currentSelection = rect;
|
|
_isAdjusting = true;
|
|
ShowControlPoints();
|
|
HintText.Text = "拖拽控制点调整选择区域,或拖拽边框移动位置";
|
|
HintTextBorder.Visibility = Visibility.Visible;
|
|
}
|
|
else
|
|
{
|
|
SelectedArea = null;
|
|
SelectionRectangle.Visibility = Visibility.Collapsed;
|
|
SizeInfoBorder.Visibility = Visibility.Collapsed;
|
|
HintText.Text = "请拖拽选择区域,右键或双击可全选";
|
|
HintTextBorder.Visibility = Visibility.Visible;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ControlPoint_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
|
{
|
|
if (!_isAdjusting) return;
|
|
|
|
_isMoving = true;
|
|
_lastMousePosition = e.GetPosition(this);
|
|
|
|
// 确定当前控制点类型
|
|
var ellipse = sender as Ellipse;
|
|
if (ellipse == TopLeftControl) _activeControlPoint = ControlPointType.TopLeft;
|
|
else if (ellipse == TopRightControl) _activeControlPoint = ControlPointType.TopRight;
|
|
else if (ellipse == BottomLeftControl) _activeControlPoint = ControlPointType.BottomLeft;
|
|
else if (ellipse == BottomRightControl) _activeControlPoint = ControlPointType.BottomRight;
|
|
else if (ellipse == TopControl) _activeControlPoint = ControlPointType.Top;
|
|
else if (ellipse == BottomControl) _activeControlPoint = ControlPointType.Bottom;
|
|
else if (ellipse == LeftControl) _activeControlPoint = ControlPointType.Left;
|
|
else if (ellipse == RightControl) _activeControlPoint = ControlPointType.Right;
|
|
|
|
// 捕获鼠标到控制点本身,而不是整个窗口
|
|
ellipse?.CaptureMouse();
|
|
e.Handled = true;
|
|
}
|
|
|
|
private void ControlPoint_MouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
if (!_isAdjusting || !_isMoving || _activeControlPoint == ControlPointType.None) return;
|
|
|
|
try
|
|
{
|
|
var currentPosition = e.GetPosition(this);
|
|
var delta = currentPosition - _lastMousePosition;
|
|
|
|
// 根据控制点类型调整选择区域
|
|
AdjustSelection(delta);
|
|
|
|
_lastMousePosition = currentPosition;
|
|
e.Handled = true;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// 如果出现异常,停止移动
|
|
_isMoving = false;
|
|
_activeControlPoint = ControlPointType.None;
|
|
var ellipse = sender as Ellipse;
|
|
ellipse?.ReleaseMouseCapture();
|
|
}
|
|
}
|
|
|
|
private void ControlPoint_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
|
{
|
|
if (_isMoving)
|
|
{
|
|
_isMoving = false;
|
|
_activeControlPoint = ControlPointType.None;
|
|
var ellipse = sender as Ellipse;
|
|
ellipse?.ReleaseMouseCapture();
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
|
|
private void AdjustSelection(Vector delta)
|
|
{
|
|
var newRect = _currentSelection;
|
|
|
|
switch (_activeControlPoint)
|
|
{
|
|
case ControlPointType.TopLeft:
|
|
newRect.X += delta.X;
|
|
newRect.Y += delta.Y;
|
|
newRect.Width -= delta.X;
|
|
newRect.Height -= delta.Y;
|
|
break;
|
|
case ControlPointType.TopRight:
|
|
newRect.Y += delta.Y;
|
|
newRect.Width += delta.X;
|
|
newRect.Height -= delta.Y;
|
|
break;
|
|
case ControlPointType.BottomLeft:
|
|
newRect.X += delta.X;
|
|
newRect.Width -= delta.X;
|
|
newRect.Height += delta.Y;
|
|
break;
|
|
case ControlPointType.BottomRight:
|
|
newRect.Width += delta.X;
|
|
newRect.Height += delta.Y;
|
|
break;
|
|
case ControlPointType.Top:
|
|
newRect.Y += delta.Y;
|
|
newRect.Height -= delta.Y;
|
|
break;
|
|
case ControlPointType.Bottom:
|
|
newRect.Height += delta.Y;
|
|
break;
|
|
case ControlPointType.Left:
|
|
newRect.X += delta.X;
|
|
newRect.Width -= delta.X;
|
|
break;
|
|
case ControlPointType.Right:
|
|
newRect.Width += delta.X;
|
|
break;
|
|
}
|
|
|
|
// 确保最小尺寸
|
|
if (newRect.Width >= 10 && newRect.Height >= 10)
|
|
{
|
|
_currentSelection = newRect;
|
|
UpdateSelectionDisplay();
|
|
}
|
|
}
|
|
|
|
private void ShowControlPoints()
|
|
{
|
|
// 确保选择矩形在调整模式下可见
|
|
SelectionRectangle.Visibility = Visibility.Visible;
|
|
// 设置选择矩形的Z-index,确保它能够接收鼠标事件
|
|
WpfCanvas.SetZIndex(SelectionRectangle, 1001);
|
|
// 确保选择矩形能够接收鼠标事件
|
|
SelectionRectangle.IsHitTestVisible = true;
|
|
ControlPointsCanvas.Visibility = Visibility.Visible;
|
|
UpdateControlPointsPosition();
|
|
}
|
|
|
|
private void UpdateControlPointsPosition()
|
|
{
|
|
var rect = _currentSelection;
|
|
|
|
// 更新角控制点位置
|
|
WpfCanvas.SetLeft(TopLeftControl, rect.Left - 4);
|
|
WpfCanvas.SetTop(TopLeftControl, rect.Top - 4);
|
|
|
|
WpfCanvas.SetLeft(TopRightControl, rect.Right - 4);
|
|
WpfCanvas.SetTop(TopRightControl, rect.Top - 4);
|
|
|
|
WpfCanvas.SetLeft(BottomLeftControl, rect.Left - 4);
|
|
WpfCanvas.SetTop(BottomLeftControl, rect.Bottom - 4);
|
|
|
|
WpfCanvas.SetLeft(BottomRightControl, rect.Right - 4);
|
|
WpfCanvas.SetTop(BottomRightControl, rect.Bottom - 4);
|
|
|
|
// 更新边控制点位置
|
|
WpfCanvas.SetLeft(TopControl, rect.Left + rect.Width / 2 - 4);
|
|
WpfCanvas.SetTop(TopControl, rect.Top - 4);
|
|
|
|
WpfCanvas.SetLeft(BottomControl, rect.Left + rect.Width / 2 - 4);
|
|
WpfCanvas.SetTop(BottomControl, rect.Bottom - 4);
|
|
|
|
WpfCanvas.SetLeft(LeftControl, rect.Left - 4);
|
|
WpfCanvas.SetTop(LeftControl, rect.Top + rect.Height / 2 - 4);
|
|
|
|
WpfCanvas.SetLeft(RightControl, rect.Right - 4);
|
|
WpfCanvas.SetTop(RightControl, rect.Top + rect.Height / 2 - 4);
|
|
}
|
|
|
|
private void UpdateSelection()
|
|
{
|
|
var rect = GetSelectionRectangle();
|
|
|
|
// 更新选择矩形
|
|
WpfCanvas.SetLeft(SelectionRectangle, rect.X);
|
|
WpfCanvas.SetTop(SelectionRectangle, rect.Y);
|
|
SelectionRectangle.Width = rect.Width;
|
|
SelectionRectangle.Height = rect.Height;
|
|
|
|
// 在选择过程中,禁用选择矩形的鼠标事件,避免干扰选择操作
|
|
if (_isSelecting)
|
|
{
|
|
SelectionRectangle.IsHitTestVisible = false;
|
|
}
|
|
|
|
// 更新透明选择区域遮罩
|
|
UpdateTransparentSelectionMask(rect);
|
|
|
|
// 更新尺寸信息
|
|
SizeInfoText.Text = $"{(int)rect.Width} x {(int)rect.Height}";
|
|
WpfCanvas.SetLeft(SizeInfoBorder, rect.X);
|
|
WpfCanvas.SetTop(SizeInfoBorder, rect.Y - 30);
|
|
|
|
// 确保尺寸信息不超出屏幕
|
|
if (WpfCanvas.GetTop(SizeInfoBorder) < 0)
|
|
{
|
|
WpfCanvas.SetTop(SizeInfoBorder, rect.Y + rect.Height + 5);
|
|
}
|
|
}
|
|
|
|
private void UpdateTransparentSelectionMask(Rect selectionRect)
|
|
{
|
|
SetSelectionMaskGeometry(new RectangleGeometry(selectionRect));
|
|
}
|
|
|
|
private void UpdateFreehandSelectionMask(IList<WpfPoint> points)
|
|
{
|
|
if (points == null || points.Count < 3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var figure = new PathFigure
|
|
{
|
|
StartPoint = points[0],
|
|
IsClosed = true,
|
|
IsFilled = true
|
|
};
|
|
|
|
var segments = new PolyLineSegment();
|
|
for (int i = 1; i < points.Count; i++)
|
|
{
|
|
segments.Points.Add(points[i]);
|
|
}
|
|
figure.Segments.Add(segments);
|
|
|
|
var geometry = new PathGeometry();
|
|
geometry.Figures.Add(figure);
|
|
SetSelectionMaskGeometry(geometry);
|
|
}
|
|
|
|
private void SetSelectionMaskGeometry(Geometry selectionGeometry)
|
|
{
|
|
try
|
|
{
|
|
if (!(TransparentSelectionMask.Clip is CombinedGeometry combinedGeometry))
|
|
{
|
|
throw new InvalidOperationException("TransparentSelectionMask.Clip 不是 CombinedGeometry");
|
|
}
|
|
|
|
combinedGeometry.Geometry2 = selectionGeometry;
|
|
|
|
// 显示透明遮罩,隐藏原始遮罩
|
|
TransparentSelectionMask.Visibility = Visibility.Visible;
|
|
OverlayRectangle.Visibility = Visibility.Collapsed;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// 如果几何体操作失败,回退到原始遮罩
|
|
TransparentSelectionMask.Visibility = Visibility.Collapsed;
|
|
OverlayRectangle.Visibility = Visibility.Visible;
|
|
}
|
|
}
|
|
|
|
private void UpdateSelectionDisplay()
|
|
{
|
|
var rect = _currentSelection;
|
|
|
|
// 更新选择矩形
|
|
WpfCanvas.SetLeft(SelectionRectangle, rect.X);
|
|
WpfCanvas.SetTop(SelectionRectangle, rect.Y);
|
|
SelectionRectangle.Width = rect.Width;
|
|
SelectionRectangle.Height = rect.Height;
|
|
|
|
// 确保选择矩形在调整模式下能够接收鼠标事件
|
|
if (_isAdjusting)
|
|
{
|
|
SelectionRectangle.IsHitTestVisible = true;
|
|
WpfCanvas.SetZIndex(SelectionRectangle, 1001);
|
|
}
|
|
|
|
// 更新透明选择区域遮罩
|
|
UpdateTransparentSelectionMask(rect);
|
|
|
|
// 更新控制点位置
|
|
UpdateControlPointsPosition();
|
|
|
|
// 更新尺寸信息
|
|
SizeInfoText.Text = $"{(int)rect.Width} x {(int)rect.Height}";
|
|
WpfCanvas.SetLeft(SizeInfoBorder, rect.X);
|
|
WpfCanvas.SetTop(SizeInfoBorder, rect.Y - 30);
|
|
|
|
// 确保尺寸信息不超出屏幕
|
|
if (WpfCanvas.GetTop(SizeInfoBorder) < 0)
|
|
{
|
|
WpfCanvas.SetTop(SizeInfoBorder, rect.Y + rect.Height + 5);
|
|
}
|
|
}
|
|
|
|
private Rect GetSelectionRectangle()
|
|
{
|
|
double x = Math.Min(_startPoint.X, _currentPoint.X);
|
|
double y = Math.Min(_startPoint.Y, _currentPoint.Y);
|
|
double width = Math.Abs(_currentPoint.X - _startPoint.X);
|
|
double height = Math.Abs(_currentPoint.Y - _startPoint.Y);
|
|
|
|
return new Rect(x, y, width, height);
|
|
}
|
|
|
|
private void ConfirmSelection()
|
|
{
|
|
// 在自由绘制模式下,走自由选区确认
|
|
if (_isFreehandMode)
|
|
{
|
|
ConfirmFreehandSelection();
|
|
return;
|
|
}
|
|
|
|
if (_isAdjusting)
|
|
{
|
|
// 转换为屏幕坐标,考虑DPI缩放
|
|
var dpiScale = GetDpiScale();
|
|
var virtualScreen = SystemInformation.VirtualScreen;
|
|
|
|
// 计算实际屏幕坐标
|
|
int screenX = (int)((_currentSelection.X * dpiScale) + virtualScreen.Left);
|
|
int screenY = (int)((_currentSelection.Y * dpiScale) + virtualScreen.Top);
|
|
int screenWidth = (int)(_currentSelection.Width * dpiScale);
|
|
int screenHeight = (int)(_currentSelection.Height * dpiScale);
|
|
|
|
SelectedArea = new DrawingRectangle(screenX, screenY, screenWidth, screenHeight);
|
|
}
|
|
|
|
if (SelectedArea.HasValue)
|
|
{
|
|
DialogResult = true;
|
|
}
|
|
else
|
|
{
|
|
HintText.Text = "请先选择截图区域";
|
|
HintTextBorder.Visibility = Visibility.Visible;
|
|
return;
|
|
}
|
|
|
|
Close();
|
|
}
|
|
|
|
private void ConfirmFreehandSelection()
|
|
{
|
|
if (!SelectedArea.HasValue || SelectedPath == null || SelectedPath.Count < 3)
|
|
{
|
|
HintText.Text = "请先拖动鼠标绘制自由选区";
|
|
HintTextBorder.Visibility = Visibility.Visible;
|
|
return;
|
|
}
|
|
|
|
DialogResult = true;
|
|
Close();
|
|
}
|
|
|
|
private void SelectFullScreenArea()
|
|
{
|
|
var virtualScreen = SystemInformation.VirtualScreen;
|
|
_currentSelection = new Rect(0, 0, ActualWidth, ActualHeight);
|
|
|
|
SelectionRectangle.Visibility = Visibility.Visible;
|
|
SelectionRectangle.IsHitTestVisible = false;
|
|
ControlPointsCanvas.Visibility = Visibility.Collapsed;
|
|
SizeInfoBorder.Visibility = Visibility.Visible;
|
|
|
|
_isAdjusting = false;
|
|
_isSelecting = false;
|
|
_isFreehandMode = false;
|
|
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235));
|
|
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128));
|
|
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128));
|
|
|
|
UpdateSelectionDisplay();
|
|
SelectedPath = null;
|
|
SelectedArea = new DrawingRectangle(virtualScreen.X, virtualScreen.Y, virtualScreen.Width, virtualScreen.Height);
|
|
|
|
HintText.Text = "已全选屏幕,点击确认截图或添加到白板";
|
|
HintTextBorder.Visibility = Visibility.Visible;
|
|
}
|
|
|
|
private void CancelSelection()
|
|
{
|
|
// 停止摄像头预览
|
|
if (_cameraService != null)
|
|
{
|
|
_cameraService.StopPreview();
|
|
}
|
|
|
|
SelectedArea = null;
|
|
SelectedPath = null;
|
|
CameraImage = null;
|
|
ShouldAddToWhiteboard = false;
|
|
DialogResult = false;
|
|
Close();
|
|
}
|
|
|
|
private Rect CalculatePathBounds(List<WpfPoint> points)
|
|
{
|
|
if (points == null || points.Count == 0)
|
|
return new Rect();
|
|
|
|
double minX = points[0].X;
|
|
double minY = points[0].Y;
|
|
double maxX = points[0].X;
|
|
double maxY = points[0].Y;
|
|
|
|
foreach (var point in points)
|
|
{
|
|
minX = Math.Min(minX, point.X);
|
|
minY = Math.Min(minY, point.Y);
|
|
maxX = Math.Max(maxX, point.X);
|
|
maxY = Math.Max(maxY, point.Y);
|
|
}
|
|
|
|
return new Rect(minX, minY, maxX - minX, maxY - minY);
|
|
}
|
|
|
|
private List<WpfPoint> OptimizePath(List<WpfPoint> points)
|
|
{
|
|
if (points == null || points.Count < 3)
|
|
return points;
|
|
|
|
var optimized = new List<WpfPoint>();
|
|
optimized.Add(points[0]);
|
|
|
|
for (int i = 1; i < points.Count - 1; i++)
|
|
{
|
|
var prev = points[i - 1];
|
|
var current = points[i];
|
|
var next = points[i + 1];
|
|
|
|
// 计算当前点到前后两点连线的距离
|
|
var distance = DistanceToLine(current, prev, next);
|
|
|
|
// 进一步降低阈值,保留更多点,确保路径质量
|
|
if (distance > 0.1) // 从0.5降低到0.1
|
|
{
|
|
optimized.Add(current);
|
|
}
|
|
}
|
|
|
|
optimized.Add(points[points.Count - 1]);
|
|
return optimized;
|
|
}
|
|
|
|
private double DistanceToLine(WpfPoint point, WpfPoint lineStart, WpfPoint lineEnd)
|
|
{
|
|
var A = point.X - lineStart.X;
|
|
var B = point.Y - lineStart.Y;
|
|
var C = lineEnd.X - lineStart.X;
|
|
var D = lineEnd.Y - lineStart.Y;
|
|
|
|
var dot = A * C + B * D;
|
|
var lenSq = C * C + D * D;
|
|
|
|
if (lenSq == 0) return Math.Sqrt(A * A + B * B);
|
|
|
|
var param = dot / lenSq;
|
|
|
|
double xx, yy;
|
|
|
|
if (param < 0)
|
|
{
|
|
xx = lineStart.X;
|
|
yy = lineStart.Y;
|
|
}
|
|
else if (param > 1)
|
|
{
|
|
xx = lineEnd.X;
|
|
yy = lineEnd.Y;
|
|
}
|
|
else
|
|
{
|
|
xx = lineStart.X + param * C;
|
|
yy = lineStart.Y + param * D;
|
|
}
|
|
|
|
var dx = point.X - xx;
|
|
var dy = point.Y - yy;
|
|
|
|
return Math.Sqrt(dx * dx + dy * dy);
|
|
}
|
|
|
|
private void SelectionRectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
|
{
|
|
if (!_isAdjusting) return;
|
|
|
|
_isMoving = true;
|
|
_activeControlPoint = ControlPointType.Move;
|
|
_lastMousePosition = e.GetPosition(this);
|
|
|
|
// 捕获鼠标到选择矩形
|
|
SelectionRectangle.CaptureMouse();
|
|
e.Handled = true;
|
|
}
|
|
|
|
private void SelectionRectangle_MouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
if (!_isAdjusting || !_isMoving || _activeControlPoint != ControlPointType.Move) return;
|
|
|
|
try
|
|
{
|
|
var currentPosition = e.GetPosition(this);
|
|
var delta = currentPosition - _lastMousePosition;
|
|
|
|
// 移动整个选择区域
|
|
var newRect = _currentSelection;
|
|
newRect.X += delta.X;
|
|
newRect.Y += delta.Y;
|
|
|
|
// 确保选择区域不会移出屏幕边界
|
|
var screenBounds = new Rect(0, 0, ActualWidth, ActualHeight);
|
|
if (newRect.Left < 0) newRect.X = 0;
|
|
if (newRect.Top < 0) newRect.Y = 0;
|
|
if (newRect.Right > screenBounds.Right) newRect.X = screenBounds.Right - newRect.Width;
|
|
if (newRect.Bottom > screenBounds.Bottom) newRect.Y = screenBounds.Bottom - newRect.Height;
|
|
|
|
_currentSelection = newRect;
|
|
UpdateSelectionDisplay();
|
|
|
|
_lastMousePosition = currentPosition;
|
|
e.Handled = true;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// 如果出现异常,停止移动
|
|
_isMoving = false;
|
|
_activeControlPoint = ControlPointType.None;
|
|
SelectionRectangle.ReleaseMouseCapture();
|
|
}
|
|
}
|
|
|
|
private void SelectionRectangle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
|
{
|
|
if (_isMoving && _activeControlPoint == ControlPointType.Move)
|
|
{
|
|
_isMoving = false;
|
|
_activeControlPoint = ControlPointType.None;
|
|
SelectionRectangle.ReleaseMouseCapture();
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
|
|
private void PerformFullScreenCapture()
|
|
{
|
|
try
|
|
{
|
|
// 获取虚拟屏幕边界
|
|
var virtualScreen = SystemInformation.VirtualScreen;
|
|
|
|
// 设置全屏截图区域
|
|
SelectedArea = new DrawingRectangle(virtualScreen.X, virtualScreen.Y, virtualScreen.Width, virtualScreen.Height);
|
|
SelectedPath = null; // 全屏截图不需要路径
|
|
|
|
// 直接关闭窗口并返回结果
|
|
DialogResult = true;
|
|
Close();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// 如果全屏截图失败,记录错误并关闭窗口
|
|
System.Diagnostics.Debug.WriteLine($"全屏截图失败: {ex.Message}");
|
|
DialogResult = false;
|
|
Close();
|
|
}
|
|
}
|
|
|
|
private void ResetSelectionState()
|
|
{
|
|
// 重置所有选择相关的状态
|
|
_isSelecting = false;
|
|
_isAdjusting = false;
|
|
_isMoving = false;
|
|
_isCameraMode = false;
|
|
_activeControlPoint = ControlPointType.None;
|
|
|
|
// 停止摄像头预览
|
|
if (_cameraService != null)
|
|
{
|
|
_cameraService.StopPreview();
|
|
}
|
|
|
|
// 清除自由绘制的内容
|
|
_freehandPoints.Clear();
|
|
_freehandPolyline.Points.Clear();
|
|
_freehandPolyline.Visibility = Visibility.Collapsed;
|
|
|
|
// 清除矩形选择的内容
|
|
SelectionRectangle.Visibility = Visibility.Collapsed;
|
|
SelectionRectangle.IsHitTestVisible = false;
|
|
ControlPointsCanvas.Visibility = Visibility.Collapsed;
|
|
SizeInfoBorder.Visibility = Visibility.Collapsed;
|
|
SelectionPath.Visibility = Visibility.Collapsed;
|
|
HintTextBorder.Visibility = Visibility.Collapsed;
|
|
|
|
// 隐藏摄像头预览
|
|
CameraPreviewBorder.Visibility = Visibility.Collapsed;
|
|
|
|
// 重置遮罩
|
|
TransparentSelectionMask.Visibility = Visibility.Collapsed;
|
|
OverlayRectangle.Visibility = Visibility.Visible;
|
|
|
|
// 释放鼠标捕获
|
|
if (IsMouseCaptured)
|
|
{
|
|
ReleaseMouseCapture();
|
|
}
|
|
|
|
// 释放选择矩形的鼠标捕获
|
|
if (SelectionRectangle.IsMouseCaptured)
|
|
{
|
|
SelectionRectangle.ReleaseMouseCapture();
|
|
}
|
|
|
|
// 重置选择区域
|
|
_currentSelection = new Rect();
|
|
SelectedArea = null;
|
|
SelectedPath = null;
|
|
CameraImage = null;
|
|
|
|
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
CameraModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
|
}
|
|
|
|
#region 摄像头旋转和分辨率控制
|
|
|
|
private void RotateLeftButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (_cameraService != null)
|
|
{
|
|
_cameraService.RotationAngle = (_cameraService.RotationAngle - 1 + 4) % 4;
|
|
UpdateRotationDisplay();
|
|
SaveCameraSettings();
|
|
}
|
|
}
|
|
|
|
private void RotateRightButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (_cameraService != null)
|
|
{
|
|
_cameraService.RotationAngle = (_cameraService.RotationAngle + 1) % 4;
|
|
UpdateRotationDisplay();
|
|
SaveCameraSettings();
|
|
}
|
|
}
|
|
|
|
private void ResolutionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
{
|
|
if (_cameraService != null && ResolutionComboBox.SelectedItem is ComboBoxItem selectedItem)
|
|
{
|
|
var resolution = selectedItem.Tag?.ToString();
|
|
if (!string.IsNullOrEmpty(resolution))
|
|
{
|
|
var parts = resolution.Split(',');
|
|
if (parts.Length == 2 &&
|
|
int.TryParse(parts[0], out int width) &&
|
|
int.TryParse(parts[1], out int height))
|
|
{
|
|
_cameraService.ResolutionWidth = width;
|
|
_cameraService.ResolutionHeight = height;
|
|
SaveCameraSettings();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateRotationDisplay()
|
|
{
|
|
if (_cameraService != null)
|
|
{
|
|
var angle = _cameraService.RotationAngle * 90;
|
|
RotationAngleText.Text = $"{angle}°";
|
|
}
|
|
}
|
|
|
|
private void SaveCameraSettings()
|
|
{
|
|
if (_cameraService != null)
|
|
{
|
|
MainWindow.Settings.Camera.RotationAngle = _cameraService.RotationAngle;
|
|
MainWindow.Settings.Camera.ResolutionWidth = _cameraService.ResolutionWidth;
|
|
MainWindow.Settings.Camera.ResolutionHeight = _cameraService.ResolutionHeight;
|
|
MainWindow.SaveSettingsToFile();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
protected override void OnClosed(EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
// 清理摄像头资源
|
|
if (_cameraService != null)
|
|
{
|
|
_cameraService.StopPreview();
|
|
_cameraService.Dispose();
|
|
_cameraService = null;
|
|
}
|
|
|
|
// 清理摄像头图像
|
|
_capturedCameraImage?.Dispose();
|
|
CameraImage?.Dispose();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogHelper.WriteLogToFile($"清理资源失败: {ex.Message}", LogHelper.LogType.Error);
|
|
}
|
|
finally
|
|
{
|
|
base.OnClosed(e);
|
|
}
|
|
}
|
|
}
|
|
}
|