鹤岗市网站建设_网站建设公司_CSS_seo优化
2026/1/7 0:07:15 网站建设 项目流程

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询