news 2026/5/14 2:09:54

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

作者头像

张小明

前端开发工程师

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/8 10:02:56

终极指南:WeChatPad如何强制开启微信平板模式实现双设备登录

终极指南&#xff1a;WeChatPad如何强制开启微信平板模式实现双设备登录 【免费下载链接】WeChatPad 强制使用微信平板模式 项目地址: https://gitcode.com/gh_mirrors/we/WeChatPad WeChatPad是一款基于Xposed框架的LSPosed模块&#xff0c;专门用于强制启用微信平板模…

作者头像 李华
网站建设 2026/5/4 19:58:59

微信平板模式消失的终极解决方案:WeChatPad项目深度解析

微信平板模式消失的终极解决方案&#xff1a;WeChatPad项目深度解析 【免费下载链接】WeChatPad 强制使用微信平板模式 项目地址: https://gitcode.com/gh_mirrors/we/WeChatPad 当微信更新到8.0.48版本后&#xff0c;许多用户惊讶地发现平板模式的关键功能神秘消失&…

作者头像 李华
网站建设 2026/5/9 5:32:24

如何用LeaguePrank轻松定制英雄联盟个人资料?5分钟搞定段位展示

如何用LeaguePrank轻松定制英雄联盟个人资料&#xff1f;5分钟搞定段位展示 【免费下载链接】LeaguePrank 项目地址: https://gitcode.com/gh_mirrors/le/LeaguePrank LeaguePrank是一款专为英雄联盟玩家设计的免费工具&#xff0c;通过简单的操作即可自定义游戏内的个…

作者头像 李华
网站建设 2026/5/9 22:59:53

转码刷leetcode_day9_筑基期_《绝境求生》

目录 目录 前言 动态规划 一、416分割等和子集 1、题目描述 示例 提示 2、简单理解&#xff1f; 3、暴力法 3.1、能不能用图示意&#xff1f; 3.2、初始化条件&#xff1f; 3.3、边界条件&#xff1f; 3.4、代码逻辑&#xff1f; 3.5、之前见过但没注意到的&…

作者头像 李华
网站建设 2026/5/9 0:36:16

高效配置PyTorch环境:Miniconda-Python3.10实战操作手册

高效配置PyTorch环境&#xff1a;Miniconda-Python3.10实战操作手册 在深度学习项目中&#xff0c;最让人头疼的往往不是模型调参&#xff0c;而是“环境配不起来”——明明代码没问题&#xff0c;却因为Python版本不对、依赖冲突或CUDA不兼容导致寸步难行。你是否也经历过这样…

作者头像 李华
网站建设 2026/5/9 6:58:51

微信多设备同步登录技术解析:告别设备切换困扰的完整方案

微信多设备同步登录技术解析&#xff1a;告别设备切换困扰的完整方案 【免费下载链接】WeChatPad 强制使用微信平板模式 项目地址: https://gitcode.com/gh_mirrors/we/WeChatPad 微信多设备登录限制是用户日常使用中的主要痛点&#xff0c;同一账号无法在多个移动设备上…

作者头像 李华