news 2026/9/30 12:54:48

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

作者头像

张小明

前端开发工程师

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_Click和NextButton_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/9/26 20:24:47

WPF + Modbus RTU 实现一个高颜值智能家居控制面板

前言工业自动化和智能家居系统中&#xff0c;Modbus RTU 是一种被广泛采用的串行通信协议。它结构简单、稳定可靠&#xff0c;常用于连接传感器、PLC、继电器等设备。然而&#xff0c;对于初学来说&#xff0c;如何将 Modbus 通信与现代化的用户界面结合起来&#xff0c;仍是一…

作者头像 李华
网站建设 2026/9/28 21:23:47

iOS微信红包助手2025:智能抢红包完全配置指南

iOS微信红包助手2025&#xff1a;智能抢红包完全配置指南 【免费下载链接】WeChatRedEnvelopesHelper iOS版微信抢红包插件,支持后台抢红包 项目地址: https://gitcode.com/gh_mirrors/we/WeChatRedEnvelopesHelper 还在为错过微信群里的红包而遗憾吗&#xff1f;2025年…

作者头像 李华
网站建设 2026/9/28 21:23:48

Conda clean清理缓存|Miniconda-Python3.10节省磁盘空间

Conda clean清理缓存&#xff5c;Miniconda-Python3.10节省磁盘空间 在AI开发和数据科学项目中&#xff0c;你是否曾遇到这样的问题&#xff1a;一个看似简单的Python环境&#xff0c;随着时间推移&#xff0c;miniconda3/pkgs/目录竟膨胀到数GB&#xff1f;更令人头疼的是&…

作者头像 李华
网站建设 2026/9/27 18:17:33

XUnity.AutoTranslator:打破语言障碍的Unity游戏自动翻译神器

XUnity.AutoTranslator&#xff1a;打破语言障碍的Unity游戏自动翻译神器 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 还在为看不懂的外文游戏而烦恼吗&#xff1f;&#x1f914; XUnity.AutoTranslat…

作者头像 李华
网站建设 2026/9/30 9:54:14

ViGEmBus实战精要:从零精通游戏控制器模拟的高效方案

ViGEmBus实战精要&#xff1a;从零精通游戏控制器模拟的高效方案 【免费下载链接】ViGEmBus 项目地址: https://gitcode.com/gh_mirrors/vig/ViGEmBus 在游戏开发和测试领域&#xff0c;虚拟控制器的需求日益增长。ViGEmBus作为Windows平台上的专业级游戏控制器模拟驱动…

作者头像 李华
网站建设 2026/9/27 7:46:03

英雄联盟段位修改终极指南:5分钟自定义你的游戏身份

英雄联盟段位修改终极指南&#xff1a;5分钟自定义你的游戏身份 【免费下载链接】LeaguePrank 项目地址: https://gitcode.com/gh_mirrors/le/LeaguePrank 想不想在英雄联盟中体验王者段位的风采&#xff1f;或者和朋友开个有趣的段位玩笑&#xff1f;LeaguePrank这款神…

作者头像 李华