WPF 截图控件(十):马赛克效果

WPF 截图控件(十):马赛克效果

标 题:WPF 截图控件(十):马赛克效果

作 者:WPFDevelopersOrg -驚鏵

原文链接[1]:https://github.com/WPFDevelopersOrg/WPFDevelopers

码云链接[2]:https://gitee.com/WPFDevelopersOrg/WPFDevelopers

距离截图控件上次修改已经过去了3年多,这次更新新增了截图控件的马赛克功能。

接着上一篇

1. 新增枚举
public enum ScreenCutMouseType { DrawMosaic }
2. 快照生成
  • TakeSnapshot:生成当前画布的快照。

private void TakeSnapshot() { _canvas.Measure(new System.Windows.Size(_canvas.ActualWidth, _canvas.ActualHeight)); _canvas.Arrange(new Rect(0, 0, _canvas.ActualWidth, _canvas.ActualHeight)); _imageSnapshot = new RenderTargetBitmap( (int)_canvas.ActualWidth, (int)_canvas.ActualHeight, 96, 96, PixelFormats.Pbgra32); _imageSnapshot.Render(_canvas); }
3. 马赛克绘制
  • DrawMosaicBlockcenter点、mosaic块大小和brush大小作为参数。它会在指定区域内绘制多个马赛克块,调用GetAreaAverageColor来获取每个块的颜色。

private void DrawMosaicBlock(Point center, int blockSize, int brushSize) { if (_imageSnapshot == null) return; int mosaicSize = blockSize; int blocksPerRow = brushSize / mosaicSize; for (int i = 0; i < blocksPerRow; i++) { for (int j = 0; j < blocksPerRow; j++) { double x = center.X - brushSize / 2 + i * mosaicSize; double y = center.Y - brushSize / 2 + j * mosaicSize; Point blockCenter = new Point(x + mosaicSize / 2, y + mosaicSize / 2); Color color = GetAreaAverageColor(blockCenter, mosaicSize); var block = new Rectangle { Width = mosaicSize, Height = mosaicSize, Fill = new SolidColorBrush(color), IsHitTestVisible = false }; Canvas.SetLeft(block, x); Canvas.SetTop(block, y); _canvas.Children.Add(block); _currentStrokeRectangles.Add(block); } } }
4. 平均颜色计算
  • GetAreaAverageColor:循环指定区域内的每个像素,计算其总红、绿、蓝值,从而得到平均颜色。

private Color GetAreaAverageColor(Point center, int areaSize) { try { double scaleX = _imageSnapshot.PixelWidth / _canvas.ActualWidth; double scaleY = _imageSnapshot.PixelHeight / _canvas.ActualHeight; int pixelX = (int)(center.X * scaleX); int pixelY = (int)(center.Y * scaleY); int halfSize = areaSize / 2; int totalR = 0, totalG = 0, totalB = 0; int count = 0; for (int dx = -halfSize; dx <= halfSize; dx++) { for (int dy = -halfSize; dy <= halfSize; dy++) { int x = pixelX + dx; int y = pixelY + dy; if (x >= 0 && x < _imageSnapshot.PixelWidth && y >= 0 && y < _imageSnapshot.PixelHeight) { byte[] pixels = newbyte[4]; _imageSnapshot.CopyPixels(new Int32Rect(x, y, 1, 1), pixels, 4, 0); totalR += pixels[2]; totalG += pixels[1]; totalB += pixels[0]; count++; } } } if (count == 0) return Colors.Gray; return Color.FromRgb( (byte)(totalR / count), (byte)(totalG / count), (byte)(totalB / count)); } catch { return Colors.Gray; } }
5. 完成当前绘制
  • 在完成绘制马赛克时,先检查当前的矩形列表是否为空,然后移除临时矩形,创建一个新的绘制容器,并将其添加到画布中,同时将其推入历史记录栈。

private void CompleteCurrentStroke() { if (_currentStrokeRectangles.Count == 0) return; RemoveTemporaryRectangles(); CreateStrokeContainer(); _canvas.Children.Add(_currentStrokeContainer); _strokeHistory.Push(_currentStrokeContainer); _currentStrokeContainer = null; _currentStrokeRectangles.Clear(); }
6. 移除临时矩形
  • 遍历当前的矩形列表并从画布中移除它们。

private void RemoveTemporaryRectangles() { foreach (var rect in _currentStrokeRectangles) { _canvas.Children.Remove(rect); } }
7. 创建绘制容器
  • 计算出所有矩形的最小和最大坐标,以确定绘制容器的尺寸与位置。之后,它创建一个带有圆角的矩形几何体,并使用CreateMosaicVisualBrush生成填充效果。

  • DrawingVisual上绘制当前的矩形,以生成马赛克效果并返回一个VisualBrush

private void CreateStrokeContainer() { if (_currentStrokeRectangles.Count == 0) return; double minX = double.MaxValue; double minY = double.MaxValue; double maxX = double.MinValue; double maxY = double.MinValue; foreach (var rect in _currentStrokeRectangles) { double x = Canvas.GetLeft(rect); double y = Canvas.GetTop(rect); minX = Math.Min(minX, x); minY = Math.Min(minY, y); maxX = Math.Max(maxX, x + rect.Width); maxY = Math.Max(maxY, y + rect.Height); } double width = maxX - minX; double height = maxY - minY; var roundedRect = CreateRoundedRectangleGeometry(width, height); var container = new Path { Data = roundedRect, IsHitTestVisible = false, Fill = CreateMosaicVisualBrush(minX, minY, width, height) }; Canvas.SetLeft(container, minX); Canvas.SetTop(container, minY); _currentStrokeContainer = container; } private Brush CreateMosaicVisualBrush(double left, double top, double width, double height) { var drawingVisual = new DrawingVisual(); using (var context = drawingVisual.RenderOpen()) { foreach (var rect in _currentStrokeRectangles) { double relativeX = Canvas.GetLeft(rect) - left; double relativeY = Canvas.GetTop(rect) - top; var rectGeometry = new RectangleGeometry( new Rect(relativeX, relativeY, rect.Width, rect.Height)); context.DrawGeometry(rect.Fill, null, rectGeometry); } } returnnew VisualBrush(drawingVisual) { Stretch = Stretch.None, AlignmentX = AlignmentX.Left, AlignmentY = AlignmentY.Top }; }
8. 撤销功能
protected override void OnPreviewKeyDown(KeyEventArgs e) { if (e.KeyStates == Keyboard.GetKeyStates(Key.Z) && Keyboard.Modifiers == ModifierKeys.Control) { UndoLastStroke(); } } private void UndoLastStroke() { if (_strokeHistory.Count > 0) { var lastStroke = _strokeHistory.Pop(); _canvas.Children.Remove(lastStroke); } }
9.XAML示例代码如下:
<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <CheckBox Margin="0,10,0,10" HorizontalAlignment="Center" Content="截图时隐藏当前窗口" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ScreenCutExample}}, Path=IsChecked}" /> <Button Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Top" Click="Button_Click" Content="截屏" /> <Button Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Top" Click="ButtonExt_Click" Content="截屏Ext" Style="{StaticResource WD.SuccessPrimaryButton}" /> <Image x:Name="myImage" Grid.Row="2" Grid.ColumnSpan="2" Stretch="Uniform" /> </Grid>
10.CSharp示例代码如下:
public partialclassScreenCutExample : UserControl { publicbool IsChecked { get { return (bool)GetValue(IsCheckedProperty); } set { SetValue(IsCheckedProperty, value); } } publicstaticreadonly DependencyProperty IsCheckedProperty = DependencyProperty.Register("IsChecked", typeof(bool), typeof(ScreenCutExample), new PropertyMetadata(false)); public ScreenCutExample() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { if (IsChecked) { App.CurrentMainWindow.WindowState = WindowState.Minimized; } ThreadPool.QueueUserWorkItem(state => { Thread.Sleep(350); Dispatcher.BeginInvoke(new Action(() => { ScreenCapture screenCapturer = new ScreenCapture(); screenCapturer.SnapCompleted += ScreenCapturer_SnapCompleted; screenCapturer.SnapCanceled += ScreenCapturer_SnapCanceled; screenCapturer.SnapSaveFullPath += ScreenCapturer_SnapSaveFullPath; screenCapturer.Capture(); })); }); } private void ScreenCapturer_SnapSaveFullPath(string text) { WPFDevelopers.Controls.MessageBox.Show($"截图路径:{text}","Info",MessageBoxImage.Information); } private void ScreenCapturer_SnapCanceled() { App.CurrentMainWindow.WindowState = WindowState.Normal; Message.Push($"{DateTime.Now} 取消截图", MessageBoxImage.Information); } private void ScreenCapturer_SnapCompleted(System.Windows.Media.Imaging.CroppedBitmap bitmap) { myImage.Source = bitmap; App.CurrentMainWindow.WindowState = WindowState.Normal; } private void ButtonExt_Click(object sender, RoutedEventArgs e) { if (IsChecked) { App.CurrentMainWindow.WindowState = WindowState.Minimized; } var screenCaptureExt = new ScreenCaptureExt(); screenCaptureExt.SnapCanceled += ScreenCaptureExt_SnapCanceled; screenCaptureExt.SnapCompleted += ScreenCaptureExt_SnapCompleted; screenCaptureExt.SnapSaveFullPath += ScreenCaptureExt_SnapSaveFullPath; } private void ScreenCaptureExt_SnapSaveFullPath(string text) { Message.Push($"截图路径:{text}", MessageBoxImage.Information); } private void ScreenCaptureExt_SnapCompleted(System.Windows.Media.Imaging.BitmapSource bitmap) { myImage.Source = bitmap; App.CurrentMainWindow.WindowState = WindowState.Normal; } private void ScreenCaptureExt_SnapCanceled() { try { if (App.CurrentMainWindow.WindowState == WindowState.Minimized) App.CurrentMainWindow.WindowState = WindowState.Normal; Message.Push($"{DateTime.Now} 取消截图", MessageBoxImage.Information); } catch (Exception ex) { WPFDevelopers.Controls.MessageBox.Show(ex.Message); } } }

GitHub 源码地址[3]

Gitee 源码地址[4]

参考资料

[1]

原文链接:https://github.com/WPFDevelopersOrg/WPFDevelopers

[2]

码云链接:https://gitee.com/WPFDevelopersOrg/WPFDevelopers

[3]

GitHub 源码地址:https://github.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers.Shared/Controls/ScreenCut/ScreenCut.cs

[4]

Gitee 源码地址:https://gitee.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers.Shared/Controls/ScreenCut/ScreenCut.cs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1120436.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度剖析智能小车PCB板原理图的最小系统构建

智能小车最小系统设计&#xff1a;从原理图到稳定运行的实战指南 你有没有遇到过这样的情况&#xff1f;PCB板焊好了&#xff0c;电源灯亮了&#xff0c;下载器也连上了——但MCU就是不跑代码&#xff0c;或者跑着跑着突然复位&#xff1f;更糟的是&#xff0c;传感器数据飘忽不…

如何在Windows上实现专业级虚拟手柄控制:ViGEmBus终极使用指南

如何在Windows上实现专业级虚拟手柄控制&#xff1a;ViGEmBus终极使用指南 【免费下载链接】ViGEmBus 项目地址: https://gitcode.com/gh_mirrors/vig/ViGEmBus 你是否曾经梦想过让任何输入设备都变成专业的游戏手柄&#xff1f;现在&#xff0c;这个梦想通过ViGEmBus虚…

Joy-Con Toolkit终极指南:3步快速上手,解锁手柄全部隐藏功能

Joy-Con Toolkit终极指南&#xff1a;3步快速上手&#xff0c;解锁手柄全部隐藏功能 【免费下载链接】jc_toolkit Joy-Con Toolkit 项目地址: https://gitcode.com/gh_mirrors/jc/jc_toolkit Joy-Con Toolkit是一款专为任天堂Switch手柄设计的开源控制工具&#xff0c;通…

专业文章仿写Prompt

专业文章仿写Prompt 【免费下载链接】WeChatRedEnvelopesHelper iOS版微信抢红包插件,支持后台抢红包 项目地址: https://gitcode.com/gh_mirrors/we/WeChatRedEnvelopesHelper 仿写核心要求 原创性保证&#xff1a; 新文章与原文结构相似度必须低于30%完全重构段落组…

小程序开发中的JS和Go的对比及用途

JS 只能写业务逻辑&#xff0c;Go 能写‘整个后端’——高并发、低延迟、重 I/O、轻内存&#xff0c;微信小程序要‘秒开秒回’&#xff0c;Go 就是目前最优解。一、JS 只能写“业务”&#xff0c;不能写“整个后端”维度Node.js&#xff08;JS&#xff09;Go高并发单线程事件循…

ViGEmBus虚拟手柄驱动:Windows游戏输入设备兼容性终极解决方案

ViGEmBus虚拟手柄驱动&#xff1a;Windows游戏输入设备兼容性终极解决方案 【免费下载链接】ViGEmBus 项目地址: https://gitcode.com/gh_mirrors/vig/ViGEmBus 还在为Windows系统游戏手柄兼容性问题而烦恼吗&#xff1f;ViGEmBus虚拟游戏控制器驱动技术正是你需要的完…

互联网大厂Java面试场景实战剧本:Spring Boot、微服务与云原生技术全解

互联网大厂Java面试场景实战剧本&#xff1a;Spring Boot、微服务与云原生技术全解场景设定&#xff1a; 互联网大厂二面大厅&#xff0c;面试官&#xff08;冷峻深沉&#xff09;与著名“水货”求职程序员谢飞机同台飙戏&#xff01;第一轮&#xff08;基础原理&电商场景&…

ms-swift支持多节点日志聚合分析训练异常问题

ms-swift 多节点日志聚合与训练异常分析实践 在大模型训练日益复杂的今天&#xff0c;一个看似简单的“训练中断”问题&#xff0c;背后可能隐藏着数百个GPU节点中某个rank的显存溢出、某条通信链路的短暂拥塞&#xff0c;或是数据预处理中的边缘异常。当团队投入数十甚至上百张…

python基于django的问卷调查管理系统_xvc14u58

目录基于Django的问卷调查管理系统概述核心功能模块技术实现细节安全与性能优化应用场景与扩展性关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;基于Django的问卷调查管理系统概述 …

通过FastStone Capture注释功能标注模型错误案例

通过FastStone Capture注释功能标注模型错误案例 在大模型研发进入深水区的今天&#xff0c;一个普遍被忽视的事实是&#xff1a;训练出一个能“跑通流程”的模型并不难&#xff0c;真正困难的是让这个模型在真实场景中稳定、可靠、可解释地输出预期结果。随着 Qwen、Llama、In…

python基于django的闲置资产租赁管理系统_102rj4g8

目录闲置资产租赁管理系统概述系统核心功能技术实现特点应用场景与优势关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;闲置资产租赁管理系统概述 该系统基于Python的Django框架开发…

C# 基于OpenCv的视觉工作流-章6-腐蚀

C# 基于OpenCv的视觉工作流-章6-腐蚀 本章目标&#xff1a; 一、卷积计算原理&#xff1b; 二、获取卷积核&#xff1b; 三、腐蚀&#xff1b;一、卷积计算原理&#xff1b; 卷积计算原理&#xff0c;如下图所示&#xff1a; 1、先定义卷积核&#xff08;3x3、5x5、7x7等&#…

完全掌握游戏翻译神器:XUnity Auto Translator深度使用手册

完全掌握游戏翻译神器&#xff1a;XUnity Auto Translator深度使用手册 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator XUnity Auto Translator是一款革命性的Unity游戏自动翻译插件&#xff0c;能够将外…

使用C#调用ms-swift提供的RESTful API接口服务

使用C#调用ms-swift提供的RESTful API接口服务 在企业智能化转型的浪潮中&#xff0c;越来越多的传统业务系统面临一个共同挑战&#xff1a;如何在不重构现有技术栈的前提下&#xff0c;快速集成大模型能力&#xff1f;尤其是在金融、政务、制造等领域&#xff0c;大量核心系统…

关于转行网络安全的一些建议

目录1.网络安全行业概况2.行业两极分化现象转行群体分析3.网络安全学习路径入门学习建议学习资料分享行业误解澄清4.就业情况面对转行的建议结语在当前就业形势下&#xff0c;不少朋友面临转行的困境。网络安全作为一个热门领域&#xff0c;自然也吸引了许多人的目光。本文将就…

python基于django的图书馆座位预约微信小程序系统_7mg5c898

目录系统概述技术架构核心功能创新点应用效果关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;系统概述 该系统是一个基于Django框架开发的图书馆座位预约微信小程序&#xff0c;旨在…

[数字信号处理-入门] 频域分析

[数字信号处理-入门] 频域分析 个人导航 知乎&#xff1a;https://www.zhihu.com/people/byzh_rc CSDN&#xff1a;https://blog.csdn.net/qq_54636039 注&#xff1a;本文仅对所述内容做了框架性引导&#xff0c;具体细节可查询其余相关资料or源码 参考文章&#xff1a;各…

使用MyBatisPlus管理ms-swift后台数据库持久层

使用 MyBatisPlus 管理 ms-swift 后台数据库持久层 在 AI 工程化落地日益深入的今天&#xff0c;一个高效的训练与部署框架不仅要能跑通模型&#xff0c;更要能管好数据。魔搭社区推出的 ms-swift 框架&#xff0c;正是为了解决从模型微调、对齐、推理到部署的全链路问题而生。…

C++ istringstream

1. istringstream是什么 2. 它的 “规则” 是什么&#xff1f; 3. 什么能分割&#xff0c;什么不能分割&#xff1f; 4. 基本用法&#xff1a;按空白符分割 5. 实践练习 6. 学习要点总结 一. istringstream是什么istringstream&#xff1a;字符串的流式读取器&#xff08;简明扼…

XUnity Auto Translator:打破语言壁垒,让外语游戏无障碍畅玩

XUnity Auto Translator&#xff1a;打破语言壁垒&#xff0c;让外语游戏无障碍畅玩 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 你是否曾经因为游戏语言不通而放弃一款心仪的作品&#xff1f;是否因为…