news 2026/5/22 9:21:25

如何在图片上绘制马赛克效果

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何在图片上绘制马赛克效果

如何在图片上绘制马赛克效果

标 题:如何在图片上绘制马赛克效果

作 者:WPFDevelopersOrg -驚鏵

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

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

需求:需要在图片上绘制马赛克效果,并切可以切换显示不同的图片。

  • 通过按钮进行切换背景图片。

  • 能够在图片上绘制马赛克效果。

1. 新增MainWindow.xaml代码如下:
  • Canvas: 我们使用Canvas控件作为绘图区域,CanvasBackground用于显示切换的背景图片。

  • Buttons: 左侧和右侧的按钮用于切换背景图片。

<wd:Window x:Class="ImageMosaic.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:ImageMosaic" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers" Title="Mosaic" Width="800" Height="450" mc:Ignorable="d"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Canvas x:Name="PART_Canvas" Grid.ColumnSpan="3"> <Canvas.Background> <ImageBrush x:Name="CanvasBackground" Stretch="UniformToFill" /> </Canvas.Background> </Canvas> <Button Grid.Column="0" Width="50" Height="50" wd:ElementHelper.IsRound="True" Background="#80000000" Click="PrevButton_Click" Style="{StaticResource WD.DefaultButton}"> <wd:PathIcon Kind="Previous" /> </Button> <Button Grid.Column="2" Width="50" Height="50" wd:ElementHelper.IsRound="True" Background="#80000000" Click="NextButton_Click" Style="{StaticResource WD.DefaultButton}"> <wd:PathIcon Kind="Next" /> </Button> </Grid> </wd:Window>
2. 新增MainWindow.xaml代码如下:
  • 字段定义:

    • _imageSnapshot: 用于存储当Image的快照,用于后面马赛克绘制。

    • _pointStart: 记录鼠标按下时的位置,用于绘制马赛克。

    • _backgroundImages: 存储加载的Image列表。

    • _currentImageIndex: 当前显示的Image索引。

  • 构造函数: 初始化控件,加载Image并设置事件。

  • LoadImages: 读取指定路径的Image并将其添加到_backgroundImages列表中。

  • UpdateBackground: 更新画布的Backgroup,处理图像切换,并在Canvas加载完毕后拍摄快照。

按钮事件:PrevButton_ClickNextButton_Click用于切换Image

鼠标事件: 处理鼠标点击移动释放事件,用于绘制马赛克效果。

TakeSnapshot方法: 创建当前画布的快照,以便后续绘制马赛克效果。

DrawMosaicBlock方法: 在鼠标拖动时绘制马赛克块,利用GetAreaAverageColor方法获取区域的平均颜色。

GetAreaAverageColor方法: 计算给定区域内的平均颜色,并返回相应的颜色值。

public partialclassMainWindow { private RenderTargetBitmap _imageSnapshot; private Point? _pointStart; private List<ImageSource> _backgroundImages = new List<ImageSource>(); privateint _currentImageIndex = 0; public MainWindow() { InitializeComponent(); LoadImages(); Loaded += OnMainWindow_Loaded; UpdateBackground(); } private void LoadImages() { _backgroundImages.Clear(); string[] imagePaths = { "08bf74e1e5922117c4be2f6735b078bb.jpg", "0943484gC8g.jpg", "104022Bj7Vj.jpg", "234506wB8aC.jpg", "1a374f1629daeb4dea9782d09b47d823.jpg" }; foreach (var path in imagePaths) { try { var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.UriSource = new Uri($"pack://application:,,,/{path}", UriKind.Absolute); bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.EndInit(); _backgroundImages.Add(bitmap); } catch (Exception ex) { } } } private void UpdateBackground() { PART_Canvas.Children.Clear(); if (_backgroundImages.Count == 0) return; if (_currentImageIndex < 0) _currentImageIndex = _backgroundImages.Count - 1; if (_currentImageIndex >= _backgroundImages.Count) _currentImageIndex = 0; CanvasBackground.ImageSource = _backgroundImages[_currentImageIndex]; if (PART_Canvas.IsLoaded) TakeSnapshot(); } private void PrevButton_Click(object sender, RoutedEventArgs e) { _currentImageIndex--; UpdateBackground(); } private void NextButton_Click(object sender, RoutedEventArgs e) { _currentImageIndex++; UpdateBackground(); } private void OnMainWindow_Loaded(object sender, RoutedEventArgs e) { TakeSnapshot(); } protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnPreviewMouseLeftButtonDown(e); _pointStart = e.GetPosition(PART_Canvas); } protected override void OnPreviewMouseMove(MouseEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed && _pointStart.HasValue) { var current = e.GetPosition(PART_Canvas); if ((current - _pointStart.Value).Length < 10) return; _pointStart = current; DrawMosaicBlock(current, 10, 20); } } protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e) { _pointStart = null; } private void TakeSnapshot() { PART_Canvas.Measure(new System.Windows.Size(PART_Canvas.ActualWidth, PART_Canvas.ActualHeight)); PART_Canvas.Arrange(new Rect(0, 0, PART_Canvas.ActualWidth, PART_Canvas.ActualHeight)); _imageSnapshot = new RenderTargetBitmap( (int)PART_Canvas.ActualWidth, (int)PART_Canvas.ActualHeight, 96, 96, PixelFormats.Pbgra32); _imageSnapshot.Render(PART_Canvas); } 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); PART_Canvas.Children.Add(block); } } } private Color GetAreaAverageColor(Point center, int areaSize) { try { double scaleX = _imageSnapshot.PixelWidth / PART_Canvas.ActualWidth; double scaleY = _imageSnapshot.PixelHeight / PART_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; } }

参考资料

[1]

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

[2]

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/23 1:13:08

HTML Drag and Drop上传文件至Miniconda-Python3.10处理

HTML拖拽上传与Miniconda-Python3.10后端处理的完整实践 在数据驱动的开发时代&#xff0c;一个常见的需求是&#xff1a;让用户能快速、直观地将本地文件交给系统进行分析。比如科研人员想上传一份CSV表格立即看到统计结果&#xff0c;或者工程师拖入一张图片触发AI模型推理。…

作者头像 李华
网站建设 2026/5/23 1:12:44

Vetur错误排查:常见问题解决方案一文说清

Vetur 翻车实录&#xff1a;从“提示失效”到“CPU 占爆”&#xff0c;一文彻底解决 Vue 开发编辑器卡顿难题你有没有过这样的经历&#xff1f;刚打开一个.vue文件&#xff0c;VS Code 就开始风扇狂转&#xff1b;输入this.想看看有哪些属性&#xff0c;结果智能提示像死机了一…

作者头像 李华
网站建设 2026/5/23 1:13:49

【AI+教育】与其给孩子铺路,不如磨练一双坚韧的脚:关于“韧性”的跨学科真相

“韧”是深植于中华传统文化的精神底色,古人早已用“疾风知劲草”(李世民《赐萧瑀》)点透核心——逆境从来都是检验韧性的试金石,这一传统智慧也为当代人的韧性修炼提供了根本指引。到了当下,“汉语盘点2025”将“韧”选为年度国内字,更印证了这一品质在不确定时代的稀缺…

作者头像 李华
网站建设 2026/5/22 3:26:43

XUnity.AutoTranslator:Unity游戏智能翻译解决方案深度解析

XUnity.AutoTranslator&#xff1a;Unity游戏智能翻译解决方案深度解析 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator XUnity.AutoTranslator是一款革命性的Unity游戏自动翻译插件&#xff0c;通过先进的…

作者头像 李华
网站建设 2026/5/20 13:54:01

合同在线编辑太容易出错?基于 OnlyOffice 的结构化编辑技术方案

一、方案背景 在企业合同管理场景中&#xff0c;传统的“在线 Word 编辑”模式存在以下核心问题&#xff1a; 合同格式、条款高度敏感&#xff0c;人工编辑极易引入错误 编辑权限粒度粗&#xff0c;无法区分“谁能改哪一部分” 编辑态、审批态、签署态不一致&#xff0c;存在…

作者头像 李华