尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

C# Avalonia 13- MoreDrawing - CustomPixelShader

C# Avalonia 13- MoreDrawing - CustomPixelShader
📅 发布时间:2026/6/19 8:25:23
C# Avalonia 13- MoreDrawing - CustomPixelShader

目前Avalonia无法继承Effect类重写,因为构造函数是internal。我们重写一个GrayscaleImage实现灰化。

 GrayscaleImage类

    public class GrayscaleImage : Control{public static readonly StyledProperty<IImage?> SourceProperty =AvaloniaProperty.Register<GrayscaleImage, IImage?>(nameof(Source));public static readonly StyledProperty<bool> IsGrayscaleProperty =AvaloniaProperty.Register<GrayscaleImage, bool>(nameof(IsGrayscale), true);public static readonly StyledProperty<Stretch> StretchProperty =AvaloniaProperty.Register<GrayscaleImage, Stretch>(nameof(Stretch), Stretch.Uniform);public IImage? Source{get => GetValue(SourceProperty);set => SetValue(SourceProperty, value);}public bool IsGrayscale{get => GetValue(IsGrayscaleProperty);set => SetValue(IsGrayscaleProperty, value);}public Stretch Stretch{get => GetValue(StretchProperty);set => SetValue(StretchProperty, value);}static GrayscaleImage(){AffectsRender<GrayscaleImage>(IsGrayscaleProperty, SourceProperty, StretchProperty);AffectsMeasure<GrayscaleImage>(SourceProperty);}protected override Size MeasureOverride(Size availableSize){if (Source is Bitmap bitmap){var sourceSize = new Size(bitmap.PixelSize.Width, bitmap.PixelSize.Height);if (double.IsInfinity(availableSize.Width) && double.IsInfinity(availableSize.Height))return sourceSize;if (Stretch == Stretch.None)return sourceSize;return CalculateStretchedSize(sourceSize, availableSize, Stretch);}return base.MeasureOverride(availableSize);}private Size CalculateStretchedSize(Size sourceSize, Size availableSize, Stretch stretch){double scaleX = 1.0;double scaleY = 1.0;bool isConstrainedWidth = !double.IsInfinity(availableSize.Width);bool isConstrainedHeight = !double.IsInfinity(availableSize.Height);if ((isConstrainedWidth || isConstrainedHeight) && sourceSize.Width > 0 && sourceSize.Height > 0){scaleX = isConstrainedWidth ? availableSize.Width / sourceSize.Width : scaleX;scaleY = isConstrainedHeight ? availableSize.Height / sourceSize.Height : scaleY;if (stretch == Stretch.Uniform){scaleX = scaleY = Math.Min(scaleX, scaleY);}else if (stretch == Stretch.UniformToFill){scaleX = scaleY = Math.Max(scaleX, scaleY);}return new Size(sourceSize.Width * scaleX, sourceSize.Height * scaleY);}return sourceSize;}public override void Render(DrawingContext context){base.Render(context);if (Source is not Bitmap bitmap || Bounds.Width <= 0 || Bounds.Height <= 0)return;context.Custom(new GrayscaleDrawOperation(new Rect(Bounds.Size), bitmap, IsGrayscale, Stretch));}private sealed class GrayscaleDrawOperation : ICustomDrawOperation{private readonly Rect BoundsRect;private readonly Bitmap BitmapSource;private readonly bool ApplyGrayscale;private readonly Stretch StretchMode;public GrayscaleDrawOperation(Rect boundsRect, Bitmap bitmap, bool applyGrayscale, Stretch stretch){BoundsRect = boundsRect;BitmapSource = bitmap;ApplyGrayscale = applyGrayscale;StretchMode = stretch;}public Rect Bounds => BoundsRect;public void Dispose(){}public bool HitTest(Point point) => BoundsRect.Contains(point);public bool Equals(ICustomDrawOperation? other){return other is GrayscaleDrawOperation op &&op.BitmapSource == BitmapSource &&op.BoundsRect == BoundsRect &&op.ApplyGrayscale == ApplyGrayscale &&op.StretchMode == StretchMode;}public void Render(ImmediateDrawingContext context){var leaseFeature = context.TryGetFeature<ISkiaSharpApiLeaseFeature>();if (leaseFeature is null)return;using var lease = leaseFeature.Lease();var canvas = lease.SkCanvas;using var skImage = ToSkImage(BitmapSource);using var paint = new SKPaint();if (ApplyGrayscale){float[] colorMatrix = {0.299f, 0.587f, 0.114f, 0, 0,0.299f, 0.587f, 0.114f, 0, 0,0.299f, 0.587f, 0.114f, 0, 0,0,      0,      0,      1, 0};paint.ColorFilter = SKColorFilter.CreateColorMatrix(colorMatrix);}var sourceSize = new SKSize(skImage.Width, skImage.Height);var destRect = CalculateDestinationRect(BoundsRect, sourceSize, StretchMode);canvas.Save();canvas.DrawImage(skImage, destRect, paint);canvas.Restore();}private SKRect CalculateDestinationRect(Rect bounds, SKSize sourceSize, Stretch stretch){double sourceWidth = sourceSize.Width;double sourceHeight = sourceSize.Height;double destWidth = bounds.Width;double destHeight = bounds.Height;if (stretch == Stretch.None){return new SKRect(0, 0, (float)sourceWidth, (float)sourceHeight);}if (stretch == Stretch.Fill){return new SKRect(0, 0, (float)destWidth, (float)destHeight);}double scaleX = destWidth / sourceWidth;double scaleY = destHeight / sourceHeight;if (stretch == Stretch.Uniform){double scale = Math.Min(scaleX, scaleY);double scaledWidth = sourceWidth * scale;double scaledHeight = sourceHeight * scale;double x = (destWidth - scaledWidth) / 2;double y = (destHeight - scaledHeight) / 2;return new SKRect((float)x, (float)y, (float)(x + scaledWidth), (float)(y + scaledHeight));}if (stretch == Stretch.UniformToFill){double scale = Math.Max(scaleX, scaleY);double scaledWidth = sourceWidth * scale;double scaledHeight = sourceHeight * scale;double x = (destWidth - scaledWidth) / 2;double y = (destHeight - scaledHeight) / 2;return new SKRect((float)x, (float)y, (float)(x + scaledWidth), (float)(y + scaledHeight));}return new SKRect(0, 0, (float)destWidth, (float)destHeight);}private static SKImage ToSkImage(Bitmap bitmap){using var memoryStream = new MemoryStream();bitmap.Save(memoryStream);memoryStream.Position = 0;return SKImage.FromEncodedData(memoryStream);}}}

CustomPixelShader.axaml代码

<Window xmlns="https://github.com/avaloniaui"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"Height="386" Width="268"x:Class="AvaloniaUI.CustomPixelShader"Title="CustomPixelShader"><StackPanel><GrayscaleImage Margin="5" Source="avares://AvaloniaUI/Resources/Images/harpsichord.jpg" IsGrayscale="{Binding #chkEffect.IsChecked}"></GrayscaleImage><CheckBox Name="chkEffect" Margin="5" Content="Effect enabled" IsChecked="True"></CheckBox></StackPanel>
</Window>

CustomPixelShader.axaml.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media;namespace AvaloniaUI;public partial class CustomPixelShader : Window
{public CustomPixelShader(){InitializeComponent();        }
}

运行效果

image

 

相关新闻

  • 使用标签Tag控制蒙太奇的触发时机-playmontageAndWait-Send GameplayEvent-WaitGameplayEvent
  • GAS_Aura-Spawn FireBolt from Event
  • Java 微服务架构中的实践与挑战

最新新闻

  • 研究生必备9款免费AI论文神器半天生成12万字带真实文献引用 - 麟书学长
  • 基于Miniblink49构建轻量级UI自动化测试框架:从原理到实践
  • 从8小时到15分钟:OpCore-Simplify如何让普通用户也能轻松配置Hackintosh?
  • 微信二次开发:JSSDK安全授权、Ticket多级缓存与动态签名防刷架构
  • 2026石河子黄金回收优质门店推荐,实时高价上门回收旧金金条 - 速递信息
  • 为什么大厂都在用Kafka?因为高并发系统根本离不开它

日新闻

  • 信任的进化:技术实现详解——如何用JavaScript构建博弈论模拟器
  • Terrakube自定义工作流:如何集成OPA、Infracost等工具扩展IaC能力
  • grunt-concurrent快速入门:5分钟学会并行运行Grunt任务

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号