Merge pull request #436 from Tayasui-rainnya/beta
feat: add '包含墨迹' toggle and ink-overlay support for area screenshots
This commit is contained in:
@@ -30,15 +30,19 @@ namespace Ink_Canvas
|
|||||||
public Bitmap CameraImage;
|
public Bitmap CameraImage;
|
||||||
public BitmapSource CameraBitmapSource;
|
public BitmapSource CameraBitmapSource;
|
||||||
public bool AddToWhiteboard;
|
public bool AddToWhiteboard;
|
||||||
|
public bool IncludeInk;
|
||||||
|
public BitmapSource InkOverlayBitmapSource;
|
||||||
|
|
||||||
public ScreenshotResult(Rectangle area, List<Point> path = null, Bitmap cameraImage = null,
|
public ScreenshotResult(Rectangle area, List<Point> path = null, Bitmap cameraImage = null,
|
||||||
BitmapSource cameraBitmapSource = null, bool addToWhiteboard = false)
|
BitmapSource cameraBitmapSource = null, bool addToWhiteboard = false, bool includeInk = false, BitmapSource inkOverlayBitmapSource = null)
|
||||||
{
|
{
|
||||||
Area = area;
|
Area = area;
|
||||||
Path = path;
|
Path = path;
|
||||||
CameraImage = cameraImage;
|
CameraImage = cameraImage;
|
||||||
CameraBitmapSource = cameraBitmapSource;
|
CameraBitmapSource = cameraBitmapSource;
|
||||||
AddToWhiteboard = addToWhiteboard;
|
AddToWhiteboard = addToWhiteboard;
|
||||||
|
IncludeInk = includeInk;
|
||||||
|
InkOverlayBitmapSource = inkOverlayBitmapSource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +64,8 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var inkOverlayPreview = CreateInkOverlayPreviewBitmapSource();
|
||||||
|
|
||||||
// 隐藏主窗口以避免截图包含窗口本身
|
// 隐藏主窗口以避免截图包含窗口本身
|
||||||
var originalVisibility = Visibility;
|
var originalVisibility = Visibility;
|
||||||
Visibility = Visibility.Hidden;
|
Visibility = Visibility.Hidden;
|
||||||
@@ -68,7 +74,7 @@ namespace Ink_Canvas
|
|||||||
await Task.Delay(200);
|
await Task.Delay(200);
|
||||||
|
|
||||||
// 启动区域选择截图
|
// 启动区域选择截图
|
||||||
var screenshotResult = await ShowScreenshotSelector();
|
var screenshotResult = await ShowScreenshotSelector(inkOverlayPreview);
|
||||||
|
|
||||||
// 恢复窗口显示
|
// 恢复窗口显示
|
||||||
Visibility = originalVisibility;
|
Visibility = originalVisibility;
|
||||||
@@ -104,11 +110,33 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (screenshotResult.Value.IncludeInk && screenshotResult.Value.InkOverlayBitmapSource != null)
|
||||||
|
{
|
||||||
|
var withInkBitmap = OverlayInkOnCapturedBitmap(finalBitmap, screenshotResult.Value.Area, screenshotResult.Value.InkOverlayBitmapSource);
|
||||||
|
if (withInkBitmap != null && withInkBitmap != finalBitmap)
|
||||||
|
{
|
||||||
|
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
|
||||||
|
{
|
||||||
|
finalBitmap.Dispose();
|
||||||
|
}
|
||||||
|
finalBitmap = withInkBitmap;
|
||||||
|
needDisposeFinalBitmap = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 如果有路径信息,应用形状遮罩
|
// 如果有路径信息,应用形状遮罩
|
||||||
if (screenshotResult.Value.Path != null && screenshotResult.Value.Path.Count > 0)
|
if (screenshotResult.Value.Path != null && screenshotResult.Value.Path.Count > 0)
|
||||||
{
|
{
|
||||||
finalBitmap = ApplyShapeMask(originalBitmap, screenshotResult.Value.Path, screenshotResult.Value.Area);
|
var maskedBitmap = ApplyShapeMask(finalBitmap, screenshotResult.Value.Path, screenshotResult.Value.Area);
|
||||||
needDisposeFinalBitmap = true; // 标记需要释放新创建的位图
|
if (maskedBitmap != null && maskedBitmap != finalBitmap)
|
||||||
|
{
|
||||||
|
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
|
||||||
|
{
|
||||||
|
finalBitmap.Dispose();
|
||||||
|
}
|
||||||
|
finalBitmap = maskedBitmap;
|
||||||
|
needDisposeFinalBitmap = true; // 标记需要释放新创建的位图
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将截图转换为WPF Image并插入到画布
|
// 将截图转换为WPF Image并插入到画布
|
||||||
@@ -190,16 +218,19 @@ namespace Ink_Canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显示截图区域选择器
|
/// 显示截图区域选择器并返回用户的截图结果(区域截图或摄像头截图)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>截图结果,包含区域、路径和摄像头截图信息</returns>
|
/// <param name="inkOverlayPreview">当用户选择包含墨迹的区域截图时,用于作为墨迹叠加的预览 <see cref="BitmapSource"/>;可为 <c>null</c>。</param>
|
||||||
|
/// <returns>若用户确认截图则返回 <see cref="ScreenshotResult"/>,否则返回 <c>null</c>。返回的结果可能为摄像头截图或区域截图,摄像头截图会包含 <see cref="ScreenshotResult.CameraBitmapSource"/> 或 <see cref="ScreenshotResult.CameraImage"/>,区域截图会包含有效的区域与路径。</returns>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// 该方法会:
|
/// 该方法会:
|
||||||
/// 1. 显示截图选择器窗口
|
/// 1. 在 UI 线程(通过 <see cref="Application.Current.Dispatcher"/>)上显示截图选择器窗口 <see cref="ScreenshotSelectorWindow"/>;
|
||||||
/// 2. 获取用户选择的区域或摄像头截图
|
/// 2. 获取用户选择的区域截图或摄像头截图;
|
||||||
/// 3. 返回截图结果
|
/// 3. 根据用户选择构建并返回 <see cref="ScreenshotResult"/>;
|
||||||
|
/// 4. 若用户取消对话框或未确认截图,返回 <c>null</c>;
|
||||||
|
/// 5. 方法内部捕获异常并记录日志(不会向调用方抛出异常),如需外部处理请调整实现以重新抛出或传回错误信息。
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private async Task<ScreenshotResult?> ShowScreenshotSelector()
|
private async Task<ScreenshotResult?> ShowScreenshotSelector(BitmapSource inkOverlayPreview = null)
|
||||||
{
|
{
|
||||||
ScreenshotResult? result = null;
|
ScreenshotResult? result = null;
|
||||||
|
|
||||||
@@ -207,7 +238,7 @@ namespace Ink_Canvas
|
|||||||
{
|
{
|
||||||
await Application.Current.Dispatcher.InvokeAsync(() =>
|
await Application.Current.Dispatcher.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
var selectorWindow = new ScreenshotSelectorWindow();
|
var selectorWindow = new ScreenshotSelectorWindow(inkOverlayPreview);
|
||||||
if (selectorWindow.ShowDialog() == true)
|
if (selectorWindow.ShowDialog() == true)
|
||||||
{
|
{
|
||||||
// 检查是否是摄像头截图
|
// 检查是否是摄像头截图
|
||||||
@@ -218,7 +249,9 @@ namespace Ink_Canvas
|
|||||||
null, // 摄像头截图不需要路径
|
null, // 摄像头截图不需要路径
|
||||||
null, // 不再使用Bitmap
|
null, // 不再使用Bitmap
|
||||||
selectorWindow.CameraBitmapSource, // 摄像头BitmapSource
|
selectorWindow.CameraBitmapSource, // 摄像头BitmapSource
|
||||||
selectorWindow.ShouldAddToWhiteboard
|
selectorWindow.ShouldAddToWhiteboard,
|
||||||
|
false,
|
||||||
|
null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (selectorWindow.CameraImage != null)
|
else if (selectorWindow.CameraImage != null)
|
||||||
@@ -228,7 +261,9 @@ namespace Ink_Canvas
|
|||||||
null, // 摄像头截图不需要路径
|
null, // 摄像头截图不需要路径
|
||||||
selectorWindow.CameraImage, // 摄像头图像
|
selectorWindow.CameraImage, // 摄像头图像
|
||||||
null,
|
null,
|
||||||
selectorWindow.ShouldAddToWhiteboard
|
selectorWindow.ShouldAddToWhiteboard,
|
||||||
|
false,
|
||||||
|
null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -238,7 +273,9 @@ namespace Ink_Canvas
|
|||||||
selectorWindow.SelectedPath,
|
selectorWindow.SelectedPath,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
selectorWindow.ShouldAddToWhiteboard
|
selectorWindow.ShouldAddToWhiteboard,
|
||||||
|
selectorWindow.IncludeInkInScreenshot,
|
||||||
|
selectorWindow.IncludeInkInScreenshot ? inkOverlayPreview : null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,6 +341,142 @@ namespace Ink_Canvas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BitmapSource CreateInkOverlayPreviewBitmapSource()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (inkCanvas == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((inkCanvas.Strokes?.Count ?? 0) == 0 && inkCanvas.Children.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inkCanvas.ActualWidth <= 0 || inkCanvas.ActualHeight <= 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var virtualScreen = SystemInformation.VirtualScreen;
|
||||||
|
var source = PresentationSource.FromVisual(inkCanvas);
|
||||||
|
var transformToDevice = source?.CompositionTarget?.TransformToDevice ?? System.Windows.Media.Matrix.Identity;
|
||||||
|
|
||||||
|
// PointToScreen 返回WPF坐标(DIP),统一转换为设备像素后再与 VirtualScreen 对齐
|
||||||
|
var inkTopLeftDip = inkCanvas.PointToScreen(new Point(0, 0));
|
||||||
|
var inkTopLeftPx = transformToDevice.Transform(inkTopLeftDip);
|
||||||
|
var offsetX = inkTopLeftPx.X - virtualScreen.Left;
|
||||||
|
var offsetY = inkTopLeftPx.Y - virtualScreen.Top;
|
||||||
|
var widthPx = inkCanvas.ActualWidth * transformToDevice.M11;
|
||||||
|
var heightPx = inkCanvas.ActualHeight * transformToDevice.M22;
|
||||||
|
|
||||||
|
var drawingVisual = new DrawingVisual();
|
||||||
|
using (var dc = drawingVisual.RenderOpen())
|
||||||
|
{
|
||||||
|
// 使用完整 InkCanvas 视觉树,确保包含图片等子元素
|
||||||
|
var visualBrush = new VisualBrush(inkCanvas)
|
||||||
|
{
|
||||||
|
Stretch = Stretch.Fill
|
||||||
|
};
|
||||||
|
dc.DrawRectangle(visualBrush, null, new Rect(offsetX, offsetY, widthPx, heightPx));
|
||||||
|
}
|
||||||
|
|
||||||
|
var rtb = new RenderTargetBitmap(
|
||||||
|
Math.Max(1, virtualScreen.Width),
|
||||||
|
Math.Max(1, virtualScreen.Height),
|
||||||
|
96,
|
||||||
|
96,
|
||||||
|
PixelFormats.Pbgra32);
|
||||||
|
rtb.Render(drawingVisual);
|
||||||
|
rtb.Freeze();
|
||||||
|
return rtb;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"创建截图墨迹预览失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap OverlayInkOnCapturedBitmap(Bitmap capturedBitmap, Rectangle captureArea, BitmapSource inkOverlayBitmapSource)
|
||||||
|
{
|
||||||
|
if (capturedBitmap == null || inkOverlayBitmapSource == null)
|
||||||
|
{
|
||||||
|
return capturedBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var virtualScreen = SystemInformation.VirtualScreen;
|
||||||
|
var sourceRect = new Rectangle(
|
||||||
|
captureArea.X - virtualScreen.X,
|
||||||
|
captureArea.Y - virtualScreen.Y,
|
||||||
|
captureArea.Width,
|
||||||
|
captureArea.Height);
|
||||||
|
|
||||||
|
sourceRect.Intersect(new Rectangle(0, 0, inkOverlayBitmapSource.PixelWidth, inkOverlayBitmapSource.PixelHeight));
|
||||||
|
if (sourceRect.Width <= 0 || sourceRect.Height <= 0)
|
||||||
|
{
|
||||||
|
return capturedBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var inkOverlayBitmap = ConvertBitmapSourceToBitmap(inkOverlayBitmapSource))
|
||||||
|
{
|
||||||
|
if (inkOverlayBitmap == null)
|
||||||
|
{
|
||||||
|
return capturedBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap resultBitmap = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
resultBitmap = new Bitmap(capturedBitmap.Width, capturedBitmap.Height, PixelFormat.Format32bppArgb);
|
||||||
|
using (var g = Graphics.FromImage(resultBitmap))
|
||||||
|
{
|
||||||
|
g.DrawImage(capturedBitmap, 0, 0, capturedBitmap.Width, capturedBitmap.Height);
|
||||||
|
|
||||||
|
var targetRect = new Rectangle(0, 0, Math.Min(sourceRect.Width, capturedBitmap.Width), Math.Min(sourceRect.Height, capturedBitmap.Height));
|
||||||
|
g.DrawImage(inkOverlayBitmap, targetRect, sourceRect, GraphicsUnit.Pixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultBitmap;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
resultBitmap?.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"叠加截图墨迹失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||||
|
return capturedBitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap ConvertBitmapSourceToBitmap(BitmapSource bitmapSource)
|
||||||
|
{
|
||||||
|
if (bitmapSource == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
var encoder = new PngBitmapEncoder();
|
||||||
|
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
|
||||||
|
encoder.Save(memoryStream);
|
||||||
|
memoryStream.Position = 0;
|
||||||
|
using (var tempBitmap = new Bitmap(memoryStream))
|
||||||
|
{
|
||||||
|
return new Bitmap(tempBitmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将截图插入到画布
|
/// 将截图插入到画布
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -162,10 +162,11 @@ namespace Ink_Canvas
|
|||||||
var originalVisibility = Visibility;
|
var originalVisibility = Visibility;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var inkOverlayPreview = CreateInkOverlayPreviewBitmapSource();
|
||||||
Visibility = Visibility.Hidden;
|
Visibility = Visibility.Hidden;
|
||||||
await Task.Delay(200);
|
await Task.Delay(200);
|
||||||
|
|
||||||
var screenshotResult = await ShowScreenshotSelector();
|
var screenshotResult = await ShowScreenshotSelector(inkOverlayPreview);
|
||||||
|
|
||||||
if (!screenshotResult.HasValue)
|
if (!screenshotResult.HasValue)
|
||||||
{
|
{
|
||||||
@@ -202,10 +203,32 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (screenshotResult.Value.IncludeInk && screenshotResult.Value.InkOverlayBitmapSource != null)
|
||||||
|
{
|
||||||
|
var withInkBitmap = OverlayInkOnCapturedBitmap(finalBitmap, screenshotResult.Value.Area, screenshotResult.Value.InkOverlayBitmapSource);
|
||||||
|
if (withInkBitmap != null && withInkBitmap != finalBitmap)
|
||||||
|
{
|
||||||
|
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
|
||||||
|
{
|
||||||
|
finalBitmap.Dispose();
|
||||||
|
}
|
||||||
|
finalBitmap = withInkBitmap;
|
||||||
|
needDisposeFinalBitmap = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (screenshotResult.Value.Path != null && screenshotResult.Value.Path.Count > 0)
|
if (screenshotResult.Value.Path != null && screenshotResult.Value.Path.Count > 0)
|
||||||
{
|
{
|
||||||
finalBitmap = ApplyShapeMask(originalBitmap, screenshotResult.Value.Path, screenshotResult.Value.Area);
|
var maskedBitmap = ApplyShapeMask(finalBitmap, screenshotResult.Value.Path, screenshotResult.Value.Area);
|
||||||
needDisposeFinalBitmap = true;
|
if (maskedBitmap != null && maskedBitmap != finalBitmap)
|
||||||
|
{
|
||||||
|
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
|
||||||
|
{
|
||||||
|
finalBitmap.Dispose();
|
||||||
|
}
|
||||||
|
finalBitmap = maskedBitmap;
|
||||||
|
needDisposeFinalBitmap = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var directory = Path.GetDirectoryName(desktopPath);
|
var directory = Path.GetDirectoryName(desktopPath);
|
||||||
@@ -275,10 +298,32 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (screenshotResult.IncludeInk && screenshotResult.InkOverlayBitmapSource != null)
|
||||||
|
{
|
||||||
|
var withInkBitmap = OverlayInkOnCapturedBitmap(finalBitmap, screenshotResult.Area, screenshotResult.InkOverlayBitmapSource);
|
||||||
|
if (withInkBitmap != null && withInkBitmap != finalBitmap)
|
||||||
|
{
|
||||||
|
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
|
||||||
|
{
|
||||||
|
finalBitmap.Dispose();
|
||||||
|
}
|
||||||
|
finalBitmap = withInkBitmap;
|
||||||
|
needDisposeFinalBitmap = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (screenshotResult.Path != null && screenshotResult.Path.Count > 0)
|
if (screenshotResult.Path != null && screenshotResult.Path.Count > 0)
|
||||||
{
|
{
|
||||||
finalBitmap = ApplyShapeMask(originalBitmap, screenshotResult.Path, screenshotResult.Area);
|
var maskedBitmap = ApplyShapeMask(finalBitmap, screenshotResult.Path, screenshotResult.Area);
|
||||||
needDisposeFinalBitmap = true;
|
if (maskedBitmap != null && maskedBitmap != finalBitmap)
|
||||||
|
{
|
||||||
|
if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
|
||||||
|
{
|
||||||
|
finalBitmap.Dispose();
|
||||||
|
}
|
||||||
|
finalBitmap = maskedBitmap;
|
||||||
|
needDisposeFinalBitmap = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitmapSourceForClipboard = ConvertBitmapToBitmapSource(finalBitmap);
|
bitmapSourceForClipboard = ConvertBitmapToBitmapSource(finalBitmap);
|
||||||
|
|||||||
@@ -36,6 +36,13 @@
|
|||||||
</Rectangle.Clip>
|
</Rectangle.Clip>
|
||||||
</Rectangle>
|
</Rectangle>
|
||||||
|
|
||||||
|
<!-- 墨迹预览层(用于“包含墨迹”预览) -->
|
||||||
|
<Image Name="InkPreviewImage"
|
||||||
|
Stretch="Fill"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Panel.ZIndex="950" />
|
||||||
|
|
||||||
<!-- 选择区域容器 -->
|
<!-- 选择区域容器 -->
|
||||||
<Canvas Name="SelectionCanvas" Panel.ZIndex="1000">
|
<Canvas Name="SelectionCanvas" Panel.ZIndex="1000">
|
||||||
<!-- 矩形选择模式 -->
|
<!-- 矩形选择模式 -->
|
||||||
@@ -141,6 +148,18 @@
|
|||||||
Background="#404040" />
|
Background="#404040" />
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
|
<CheckBox Name="IncludeInkCheckBox"
|
||||||
|
Content="包含墨迹"
|
||||||
|
Margin="4,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="White"
|
||||||
|
Checked="IncludeInkCheckBox_Checked"
|
||||||
|
Unchecked="IncludeInkCheckBox_Unchecked" />
|
||||||
|
<Rectangle Name="ToolbarDividerRectangle"
|
||||||
|
Width="1"
|
||||||
|
Height="20"
|
||||||
|
Fill="#404040"
|
||||||
|
Margin="8,0" />
|
||||||
<Button Name="ConfirmButton"
|
<Button Name="ConfirmButton"
|
||||||
Content="确认截图"
|
Content="确认截图"
|
||||||
Margin="4,0"
|
Margin="4,0"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Windows.Controls;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using Brushes = System.Windows.Media.Brushes;
|
using Brushes = System.Windows.Media.Brushes;
|
||||||
@@ -37,6 +38,7 @@ namespace Ink_Canvas
|
|||||||
private Bitmap _capturedCameraImage = null;
|
private Bitmap _capturedCameraImage = null;
|
||||||
private DateTime _lastBlankClickTime = DateTime.MinValue;
|
private DateTime _lastBlankClickTime = DateTime.MinValue;
|
||||||
private WpfPoint _lastBlankClickPosition;
|
private WpfPoint _lastBlankClickPosition;
|
||||||
|
private readonly BitmapSource _inkOverlayPreview;
|
||||||
|
|
||||||
private const int DoubleClickTimeThresholdMs = 300; // 双击判定时间阈值(常见范围 200~500ms)
|
private const int DoubleClickTimeThresholdMs = 300; // 双击判定时间阈值(常见范围 200~500ms)
|
||||||
private const double DoubleClickDistanceThresholdPx = 12; // 双击判定位置阈值(像素)
|
private const double DoubleClickDistanceThresholdPx = 12; // 双击判定位置阈值(像素)
|
||||||
@@ -55,9 +57,11 @@ namespace Ink_Canvas
|
|||||||
public Bitmap CameraImage { get; private set; }
|
public Bitmap CameraImage { get; private set; }
|
||||||
public System.Windows.Media.Imaging.BitmapSource CameraBitmapSource { get; private set; }
|
public System.Windows.Media.Imaging.BitmapSource CameraBitmapSource { get; private set; }
|
||||||
public bool ShouldAddToWhiteboard { get; private set; }
|
public bool ShouldAddToWhiteboard { get; private set; }
|
||||||
|
public bool IncludeInkInScreenshot { get; private set; }
|
||||||
|
|
||||||
public ScreenshotSelectorWindow()
|
public ScreenshotSelectorWindow(BitmapSource inkOverlayPreview = null)
|
||||||
{
|
{
|
||||||
|
_inkOverlayPreview = inkOverlayPreview;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
// 设置窗口覆盖所有屏幕
|
// 设置窗口覆盖所有屏幕
|
||||||
@@ -71,6 +75,7 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
// 初始化按钮状态
|
// 初始化按钮状态
|
||||||
InitializeButtonStates();
|
InitializeButtonStates();
|
||||||
|
InitializeInkPreview();
|
||||||
|
|
||||||
// 初始化摄像头服务
|
// 初始化摄像头服务
|
||||||
InitializeCameraService();
|
InitializeCameraService();
|
||||||
@@ -106,6 +111,44 @@ namespace Ink_Canvas
|
|||||||
CameraModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
CameraModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InitializeInkPreview()
|
||||||
|
{
|
||||||
|
IncludeInkInScreenshot = false;
|
||||||
|
IncludeInkCheckBox.IsChecked = false;
|
||||||
|
|
||||||
|
if (_inkOverlayPreview != null)
|
||||||
|
{
|
||||||
|
InkPreviewImage.Source = _inkOverlayPreview;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IncludeInkCheckBox.IsEnabled = false;
|
||||||
|
IncludeInkCheckBox.Opacity = 0.6;
|
||||||
|
IncludeInkCheckBox.ToolTip = "当前无可预览墨迹";
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateInkPreviewVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IncludeInkCheckBox_Checked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
IncludeInkInScreenshot = true;
|
||||||
|
UpdateInkPreviewVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IncludeInkCheckBox_Unchecked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
IncludeInkInScreenshot = false;
|
||||||
|
UpdateInkPreviewVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateInkPreviewVisibility()
|
||||||
|
{
|
||||||
|
InkPreviewImage.Visibility = IncludeInkInScreenshot && _inkOverlayPreview != null
|
||||||
|
? Visibility.Visible
|
||||||
|
: Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
private void InitializeCameraService()
|
private void InitializeCameraService()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -370,6 +413,7 @@ namespace Ink_Canvas
|
|||||||
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
|
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
|
||||||
FreehandModeButton.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)); // 灰色
|
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
||||||
|
IncludeInkCheckBox.IsEnabled = _inkOverlayPreview != null;
|
||||||
HintText.Text = "拖拽鼠标选择矩形区域";
|
HintText.Text = "拖拽鼠标选择矩形区域";
|
||||||
HintTextBorder.Visibility = Visibility.Visible;
|
HintTextBorder.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
@@ -383,6 +427,7 @@ namespace Ink_Canvas
|
|||||||
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
|
FreehandModeButton.Background = new SolidColorBrush(Color.FromRgb(37, 99, 235)); // 蓝色
|
||||||
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
RectangleModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
||||||
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
FullScreenButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
||||||
|
IncludeInkCheckBox.IsEnabled = _inkOverlayPreview != null;
|
||||||
HintText.Text = "按住鼠标左键自由绘制,松开后可继续调整或重新绘制,确认后再截图";
|
HintText.Text = "按住鼠标左键自由绘制,松开后可继续调整或重新绘制,确认后再截图";
|
||||||
HintTextBorder.Visibility = Visibility.Visible;
|
HintTextBorder.Visibility = Visibility.Visible;
|
||||||
}
|
}
|
||||||
@@ -402,6 +447,7 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
// 隐藏摄像头预览
|
// 隐藏摄像头预览
|
||||||
CameraPreviewBorder.Visibility = Visibility.Collapsed;
|
CameraPreviewBorder.Visibility = Visibility.Collapsed;
|
||||||
|
IncludeInkCheckBox.IsEnabled = _inkOverlayPreview != null;
|
||||||
|
|
||||||
// 直接执行全屏截图
|
// 直接执行全屏截图
|
||||||
PerformFullScreenCapture();
|
PerformFullScreenCapture();
|
||||||
@@ -426,6 +472,8 @@ namespace Ink_Canvas
|
|||||||
CameraPreviewBorder.Visibility = Visibility.Visible;
|
CameraPreviewBorder.Visibility = Visibility.Visible;
|
||||||
HintText.Text = "摄像头预览模式,点击确认截图按钮进行截图";
|
HintText.Text = "摄像头预览模式,点击确认截图按钮进行截图";
|
||||||
HintTextBorder.Visibility = Visibility.Visible;
|
HintTextBorder.Visibility = Visibility.Visible;
|
||||||
|
IncludeInkCheckBox.IsEnabled = false;
|
||||||
|
IncludeInkCheckBox.IsChecked = false;
|
||||||
|
|
||||||
// 启动摄像头预览
|
// 启动摄像头预览
|
||||||
if (_cameraService != null && _cameraService.HasAvailableCameras())
|
if (_cameraService != null && _cameraService.HasAvailableCameras())
|
||||||
@@ -491,6 +539,7 @@ namespace Ink_Canvas
|
|||||||
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
|
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ShouldAddToWhiteboard = false;
|
ShouldAddToWhiteboard = false;
|
||||||
|
IncludeInkInScreenshot = IncludeInkCheckBox.IsChecked == true;
|
||||||
|
|
||||||
// 在自由绘制模式下,按当前自由选区执行确认
|
// 在自由绘制模式下,按当前自由选区执行确认
|
||||||
if (_isFreehandMode)
|
if (_isFreehandMode)
|
||||||
@@ -512,6 +561,7 @@ namespace Ink_Canvas
|
|||||||
private void AddToWhiteboardButton_Click(object sender, RoutedEventArgs e)
|
private void AddToWhiteboardButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ShouldAddToWhiteboard = true;
|
ShouldAddToWhiteboard = true;
|
||||||
|
IncludeInkInScreenshot = IncludeInkCheckBox.IsChecked == true;
|
||||||
|
|
||||||
// 在自由绘制模式下,按当前自由选区执行确认
|
// 在自由绘制模式下,按当前自由选区执行确认
|
||||||
if (_isFreehandMode)
|
if (_isFreehandMode)
|
||||||
@@ -578,10 +628,12 @@ namespace Ink_Canvas
|
|||||||
if (hitElement != null && (
|
if (hitElement != null && (
|
||||||
hitElement is Ellipse ||
|
hitElement is Ellipse ||
|
||||||
hitElement is System.Windows.Controls.Button ||
|
hitElement is System.Windows.Controls.Button ||
|
||||||
|
hitElement is System.Windows.Controls.CheckBox ||
|
||||||
hitElement is Border ||
|
hitElement is Border ||
|
||||||
hitElement is TextBlock ||
|
hitElement is TextBlock ||
|
||||||
hitElement is StackPanel ||
|
hitElement is StackPanel ||
|
||||||
hitElement is Separator ||
|
hitElement is Separator ||
|
||||||
|
hitElement.Name == "ToolbarDividerRectangle" ||
|
||||||
hitElement.Name == "SizeInfoBorder" ||
|
hitElement.Name == "SizeInfoBorder" ||
|
||||||
hitElement.Name == "HintText" ||
|
hitElement.Name == "HintText" ||
|
||||||
hitElement.Name == "AdjustModeHint" ||
|
hitElement.Name == "AdjustModeHint" ||
|
||||||
@@ -669,10 +721,12 @@ namespace Ink_Canvas
|
|||||||
if (hitElement != null && (
|
if (hitElement != null && (
|
||||||
hitElement is Ellipse ||
|
hitElement is Ellipse ||
|
||||||
hitElement is System.Windows.Controls.Button ||
|
hitElement is System.Windows.Controls.Button ||
|
||||||
|
hitElement is System.Windows.Controls.CheckBox ||
|
||||||
hitElement is Border ||
|
hitElement is Border ||
|
||||||
hitElement is TextBlock ||
|
hitElement is TextBlock ||
|
||||||
hitElement is StackPanel ||
|
hitElement is StackPanel ||
|
||||||
hitElement is Separator ||
|
hitElement is Separator ||
|
||||||
|
hitElement.Name == "ToolbarDividerRectangle" ||
|
||||||
hitElement.Name == "SizeInfoBorder" ||
|
hitElement.Name == "SizeInfoBorder" ||
|
||||||
hitElement.Name == "HintText" ||
|
hitElement.Name == "HintText" ||
|
||||||
hitElement.Name == "AdjustModeHint"))
|
hitElement.Name == "AdjustModeHint"))
|
||||||
@@ -1178,6 +1232,7 @@ namespace Ink_Canvas
|
|||||||
SelectedPath = null;
|
SelectedPath = null;
|
||||||
CameraImage = null;
|
CameraImage = null;
|
||||||
ShouldAddToWhiteboard = false;
|
ShouldAddToWhiteboard = false;
|
||||||
|
IncludeInkInScreenshot = false;
|
||||||
DialogResult = false;
|
DialogResult = false;
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user